diff options
| author | Ayose <ayosec@gmail.com> | 2021-09-23 22:11:02 +0100 |
|---|---|---|
| committer | Ayose <ayosec@gmail.com> | 2021-09-23 22:11:02 +0100 |
| commit | 91b85669b88492bb10b832d2dc6df7641a26d3db (patch) | |
| tree | 89443c85ad9396c9ac03bc3ec4689498427b5f20 | |
| parent | 699a7a9d13fd741bf4430514ac8d6f579a68484a (diff) | |
| parent | 58985a4dcbe464230b5d2566ee68e2d34a1788c8 (diff) | |
| download | r-alacritty-91b85669b88492bb10b832d2dc6df7641a26d3db.tar.gz r-alacritty-91b85669b88492bb10b832d2dc6df7641a26d3db.tar.bz2 r-alacritty-91b85669b88492bb10b832d2dc6df7641a26d3db.zip | |
Merge remote-tracking branch 'vendor/master' into graphics
50 files changed, 916 insertions, 565 deletions
diff --git a/.agignore b/.agignore deleted file mode 100644 index 2231d1fa..00000000 --- a/.agignore +++ /dev/null @@ -1 +0,0 @@ -tests/ref diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 70ee6d43..88232e5d 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -8,6 +8,7 @@ packages: - x11-fonts/fontconfig - x11-fonts/dejavu - x11/libxcb + - x11/libxkbcommon sources: - https://github.com/alacritty/alacritty @@ -23,8 +24,8 @@ tasks: cargo test - oldstable: | cd alacritty - rustup toolchain install --profile minimal 1.45.2 - rustup default 1.45.2 + rustup toolchain install --profile minimal 1.46.0 + rustup default 1.46.0 cargo test - clippy: | cd alacritty diff --git a/.builds/linux.yml b/.builds/linux.yml index 3f793175..3dc36553 100644 --- a/.builds/linux.yml +++ b/.builds/linux.yml @@ -7,6 +7,7 @@ packages: - freetype2 - fontconfig - libxcb + - libxkbcommon sources: - https://github.com/alacritty/alacritty @@ -26,8 +27,8 @@ tasks: cargo +nightly fmt -- --check - oldstable: | cd alacritty - rustup toolchain install --profile minimal 1.45.2 - rustup default 1.45.2 + rustup toolchain install --profile minimal 1.46.0 + rustup default 1.46.0 cargo test - clippy: | cd alacritty diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 3d432e03..00000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -/CHANGELOG.md merge=union diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23379ab5..f0b713cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,9 +19,17 @@ jobs: run: cargo test - name: Oldstable run: | - rustup default 1.45.2 + rustup default 1.46.0 cargo test - name: Clippy run: | rustup component add clippy cargo clippy --all-targets + check-macos-arm: + runs-on: macos-11 + steps: + - uses: actions/checkout@v2 + - name: Install target + run: rustup update && rustup target add aarch64-apple-darwin + - name: Check build + run: cargo check --target=aarch64-apple-darwin diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 30085858..ae8dc448 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,14 +10,18 @@ env: jobs: macos: - runs-on: macos-latest + runs-on: macos-11 steps: - uses: actions/checkout@v2 + - name: Install ARM target + run: rustup update && rustup target add aarch64-apple-darwin - name: Test run: cargo test --release - - name: Make App - run: make dmg + - name: Test ARM + run: cargo test --release --target=aarch64-apple-darwin + - name: Make DMG + run: make dmg-universal - name: Upload Application run: | mv ./target/release/osx/Alacritty.dmg ./Alacritty-${GITHUB_REF##*/}.dmg @@ -63,7 +67,7 @@ jobs: - name: Install dependencies run: | sudo apt-get install cmake pkg-config libfreetype6-dev libfontconfig1-dev \ - libxcb-xfixes0-dev python3 + libxcb-xfixes0-dev libxkbcommon-dev python3 - name: Test run: cargo test --release - name: Gzip manpage diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ad3d89a..269d4537 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,23 @@ The sections should follow the order `Packaging`, `Added`, `Changed`, `Fixed` an The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -## 0.9.0-dev +## 0.10.0-dev + +### Added + +- Option `colors.transparent_background_colors` to allow applying opacity to all background colors + +### Changed + +- `ExpandSelection` is now a configurable mouse binding action +- Config option `background_opacity`, you should use `window.opacity` instead +- Reload configuration files when their symbolic link is replaced + +## 0.9.0 + +### Packaging + +- Minimum Rust version has been bumped to 1.46.0 ### Added @@ -16,7 +32,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Regression in rendering performance with dense grids since 0.6.0 - Crash/Freezes with partially visible fullwidth characters due to alt screen resize -- Incorrect vi cursor position after invoking `ScrollPageHalfUp` action +- Incorrect vi cursor position after invoking `ScrollPage*` action +- Slow PTY read performance with extremely dense grids +- Crash when resizing during vi mode +- Unintentional text selection range change after leaving vi mode +- Deadlock on Windows during high frequency output +- Search without vi mode not starting at the correct location when scrolled into history +- Crash when starting a vi mode search from the bottommost line +- Original scroll position not restored after canceling search +- Clipboard copy skipping non-empty cells when encountering an interrupted tab character +- Vi mode cursor moving downward when scrolled in history with active output +- Crash when moving fullwidth characters off the side of the terminal in insert mode +- Broken bitmap font rendering with FreeType 2.11+ +- Crash with non-utf8 font paths on Linux +- Newly installed fonts not rendering until Alacritty restart ## 0.8.0 @@ -24,6 +53,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Minimum Rust version has been bumped to 1.45.0 +### Packaging + +- Updated shell completions +- Added ARM executable to prebuilt macOS binaries + ### Added - IME composition preview not appearing on Windows diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 351bb5de..5794fc8a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,7 +42,7 @@ and [easy](https://github.com/alacritty/alacritty/issues?q=is%3Aopen+is%3Aissue+label%3A%22D+-+easy%22) issues. -Please note that the minimum supported version of Alacritty is Rust 1.43.0. All patches are expected +Please note that the minimum supported version of Alacritty is Rust 1.46.0. All patches are expected to work with the minimum supported version. Since `alacritty_terminal`'s version always tracks the next release, make sure that the version is @@ -16,12 +16,11 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "alacritty" -version = "0.9.0-dev" +version = "0.10.0-dev" dependencies = [ "alacritty_config_derive", "alacritty_terminal", "bitflags", - "clap", "cocoa 0.24.0", "copypasta", "crossfont", @@ -41,6 +40,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml", + "structopt", "time", "unicode-width", "wayland-client", @@ -63,7 +63,7 @@ dependencies = [ [[package]] name = "alacritty_terminal" -version = "0.13.1-dev" +version = "0.15.1-dev" dependencies = [ "alacritty_config_derive", "base64", @@ -75,7 +75,7 @@ dependencies = [ "mio-anonymous-pipes", "mio-extras", "miow 0.3.7", - "nix 0.20.0", + "nix 0.22.0", "parking_lot", "regex-automata", "serde", @@ -151,12 +151,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] name = "calloop" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -168,9 +162,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" +checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" [[package]] name = "cfg-if" @@ -393,9 +387,9 @@ dependencies = [ [[package]] name = "crossfont" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc52c750165ecb41cf02bfa8fd6731dc6796dddd87a3713fc9fbe9e08e71d48b" +checksum = "b6c0967e93a0440865bf1d867c3a50d6993f5054b2a10186fc2830397918241d" dependencies = [ "cocoa 0.24.0", "core-foundation 0.9.1", @@ -530,9 +524,9 @@ dependencies = [ [[package]] name = "embed-resource" -version = "1.6.2" +version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0ea6debf1262982d24274dc85f3374b42534df140897c25cea86b81e017d470" +checksum = "45de30eb317b4cd3882ee16623cb3004e5fb99a8f4cd40097cadf61efbc54adc" dependencies = [ "cc", "vswhom", @@ -668,9 +662,9 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "getrandom" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if 1.0.0", "libc", @@ -761,10 +755,19 @@ dependencies = [ ] [[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] name = "hermit-abi" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] @@ -797,9 +800,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" dependencies = [ "cfg-if 1.0.0", ] @@ -861,9 +864,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.94" +version = "0.2.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" [[package]] name = "libloading" @@ -927,9 +930,9 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memchr" -version = "2.4.0" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "memmap2" @@ -941,10 +944,19 @@ dependencies = [ ] [[package]] +name = "memmap2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +dependencies = [ + "libc", +] + +[[package]] name = "memoffset" -version = "0.6.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" dependencies = [ "autocfg", ] @@ -979,12 +991,13 @@ dependencies = [ [[package]] name = "mio-anonymous-pipes" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c274c3c52dcd1d78c5d7ed841eca1e9ea2db8353f3b8ec25789cc62c471aaf" +checksum = "6bc513025fe5005a3aa561b50fdb2cda5a150b84800ae02acd8aa9ed62ca1a6b" dependencies = [ "mio", "miow 0.3.7", + "parking_lot", "spsc-buffer", "winapi 0.3.9", ] @@ -1103,10 +1116,23 @@ dependencies = [ ] [[package]] +name = "nix" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1e25ee6b412c2a1e3fcb6a4499a5c1bfe7f43e014bdce9a6b6666e5aa2d187" +dependencies = [ + "bitflags", + "cc", + "cfg-if 1.0.0", + "libc", + "memoffset", +] + +[[package]] name = "nom" -version = "6.1.2" +version = "6.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" +checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6" dependencies = [ "memchr", "version_check", @@ -1114,9 +1140,9 @@ dependencies = [ [[package]] name = "notify" -version = "4.0.16" +version = "4.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2599080e87c9bd051ddb11b10074f4da7b1223298df65d4c2ec5bcf309af1533" +checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257" dependencies = [ "bitflags", "filetime", @@ -1183,9 +1209,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" [[package]] name = "osmesa-sys" @@ -1263,10 +1289,34 @@ dependencies = [ ] [[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] name = "proc-macro2" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" dependencies = [ "unicode-xid", ] @@ -1291,9 +1341,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85dd92e586f7355c633911e11f77f3d12f04b1b1bd76a198bd34ae3af8341ef2" +checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" dependencies = [ "bitflags", ] @@ -1310,19 +1360,18 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "byteorder", "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.24" +version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00efb87459ba4f6fb2169d20f68565555688e1250ee6825cdf6254f8b48fafb2" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "rusttype" @@ -1363,18 +1412,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.125" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.125" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc" dependencies = [ "proc-macro2", "quote", @@ -1383,9 +1432,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.64" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +checksum = "336b10da19a12ad094b59d870ebde26a45402e5b470add4b5fd03c5048a32127" dependencies = [ "itoa", "ryu", @@ -1448,9 +1497,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" dependencies = [ "libc", ] @@ -1479,7 +1528,7 @@ dependencies = [ "dlib 0.4.2", "lazy_static", "log", - "memmap2", + "memmap2 0.1.0", "nix 0.18.0", "wayland-client", "wayland-cursor", @@ -1487,12 +1536,29 @@ dependencies = [ ] [[package]] +name = "smithay-client-toolkit" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec783683499a2cfc85b6df3d04f83b1907b5cbd98a1aed44667dbdf1eac4e64c" +dependencies = [ + "bitflags", + "dlib 0.5.0", + "lazy_static", + "log", + "memmap2 0.2.3", + "nix 0.20.0", + "wayland-client", + "wayland-cursor", + "wayland-protocols", +] + +[[package]] name = "smithay-clipboard" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06384dfaf645908220d976ae24ed39f6cf92efecb0225ea0a948e403014de527" +checksum = "986c5b4a7bd4f50d4c51f81f844745535cb488360f9cf63293780b109b9295f3" dependencies = [ - "smithay-client-toolkit", + "smithay-client-toolkit 0.14.0", "wayland-client", ] @@ -1515,10 +1581,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" [[package]] +name = "structopt" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b041cdcb67226aca307e6e7be44c8806423d83e018bd662360a93dabce4d71" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7813934aecf5f51a54775e00068c237de98489463968231a51746bbbc03f9c10" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "syn" -version = "1.0.71" +version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad184cc9470f9117b2ac6817bfe297307418819ba40552f9b3846f05c33d5373" +checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" dependencies = [ "proc-macro2", "quote", @@ -1536,18 +1626,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" +checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" +checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" dependencies = [ "proc-macro2", "quote", @@ -1580,6 +1670,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" [[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] name = "unicode-width" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1668,9 +1764,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wayland-client" -version = "0.28.5" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ca44d86554b85cf449f1557edc6cc7da935cc748c8e4bf1c507cbd43bae02c" +checksum = "e3ab332350e502f159382201394a78e3cc12d0f04db863429260164ea40e0355" dependencies = [ "bitflags", "downcast-rs", @@ -1684,9 +1780,9 @@ dependencies = [ [[package]] name = "wayland-commons" -version = "0.28.5" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd75ae380325dbcff2707f0cd9869827ea1d2d6d534cff076858d3f0460fd5a" +checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda" dependencies = [ "nix 0.20.0", "once_cell", @@ -1696,9 +1792,9 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.28.5" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b37e5455ec72f5de555ec39b5c3704036ac07c2ecd50d0bffe02d5fe2d4e65ab" +checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a" dependencies = [ "nix 0.20.0", "wayland-client", @@ -1707,9 +1803,9 @@ dependencies = [ [[package]] name = "wayland-egl" -version = "0.28.5" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9461a67930ec16da7a4fd8b50e9ffa23f4417240b43ec84008bd1b2c94421c94" +checksum = "99ba1ab1e18756b23982d36f08856d521d7df45015f404a2d7c4f0b2d2f66956" dependencies = [ "wayland-client", "wayland-sys", @@ -1717,9 +1813,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.28.5" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95df3317872bcf9eec096c864b69aa4769a1d5d6291a5b513f8ba0af0efbd52c" +checksum = "286620ea4d803bacf61fa087a4242ee316693099ee5a140796aaba02b29f861f" dependencies = [ "bitflags", "wayland-client", @@ -1729,9 +1825,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.28.5" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389d680d7bd67512dc9c37f39560224327038deb0f0e8d33f870900441b68720" +checksum = "ce923eb2deb61de332d1f356ec7b6bf37094dc5573952e1c8936db03b54c03f1" dependencies = [ "proc-macro2", "quote", @@ -1740,9 +1836,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.28.5" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2907bd297eef464a95ba9349ea771611771aa285b932526c633dc94d5400a8e2" +checksum = "d841fca9aed7febf9bed2e9796c49bf58d4152ceda8ac949ebe00868d8f0feb8" dependencies = [ "dlib 0.5.0", "lazy_static", @@ -1818,7 +1914,7 @@ dependencies = [ "percent-encoding", "raw-window-handle", "serde", - "smithay-client-toolkit", + "smithay-client-toolkit 0.12.3", "wayland-client", "winapi 0.3.9", "x11-dl", @@ -1826,9 +1922,9 @@ dependencies = [ [[package]] name = "winreg" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d107f8c6e916235c4c01cabb3e8acf7bea8ef6a63ca2e7fa0527c049badfc48c" +checksum = "16cdb3898397cf7f624c294948669beafaeebc5577d5ec53d0afb76633593597" dependencies = [ "winapi 0.3.9", ] @@ -1854,9 +1950,9 @@ dependencies = [ [[package]] name = "x11-clipboard" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5e937afd03b64b7be4f959cc044e09260a47241b71e56933f37db097bf7859d" +checksum = "b397ace6e980510de59a4fe3d4c758dffab231d6d747ce9fa1aba6b6035d5f32" dependencies = [ "xcb", ] @@ -1900,9 +1996,9 @@ checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" [[package]] name = "xml-rs" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" +checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" [[package]] name = "yaml-rust" @@ -9,6 +9,9 @@ install it directly through cargo: cargo install alacritty ``` +Note that you will still need to install the dependencies for your OS of choice. +Please refer to the [Dependencies](#dependencies) section. + # Manual Installation 1. [Prerequisites](#prerequisites) @@ -81,7 +84,7 @@ to build Alacritty. Here's an apt command that should install all of them. If something is still found to be missing, please open an issue. ```sh -apt-get install cmake pkg-config libfreetype6-dev libfontconfig1-dev libxcb-xfixes0-dev python3 +apt-get install cmake pkg-config libfreetype6-dev libfontconfig1-dev libxcb-xfixes0-dev libxkbcommon-dev python3 ``` #### Arch Linux @@ -91,7 +94,7 @@ On Arch Linux, you need a few extra libraries to build Alacritty. Here's a to be missing, please open an issue. ```sh -pacman -S cmake freetype2 fontconfig pkg-config make libxcb +pacman -S cmake freetype2 fontconfig pkg-config make libxcb libxkbcommon python ``` #### Fedora @@ -101,7 +104,7 @@ command that should install all of them. If something is still found to be missing, please open an issue. ```sh -dnf install cmake freetype-devel fontconfig-devel libxcb-devel g++ +dnf install cmake freetype-devel fontconfig-devel libxcb-devel libxkbcommon-devel g++ ``` #### CentOS/RHEL 7 @@ -111,7 +114,7 @@ command that should install all of them. If something is still found to be missing, please open an issue. ```sh -yum install cmake freetype-devel fontconfig-devel libxcb-devel xcb-util-devel +yum install cmake freetype-devel fontconfig-devel libxcb-devel libxkbcommon-devel xcb-util-devel yum group install "Development Tools" ``` @@ -122,7 +125,7 @@ a `zypper` command that should install all of them. If something is still found to be missing, please open an issue. ```sh -zypper install cmake freetype-devel fontconfig-devel libxcb-devel +zypper install cmake freetype-devel fontconfig-devel libxcb-devel libxkbcommon-dev ``` #### Slackware @@ -131,7 +134,7 @@ Compiles out of the box for 14.2 #### Void Linux -On [Void Linux](https://voidlinux.eu), install following packages before +On [Void Linux](https://voidlinux.org), install following packages before compiling Alacritty: ```sh @@ -145,7 +148,7 @@ command that should install all of them. If something is still found to be missing, please open an issue. ```sh -pkg install cmake freetype2 fontconfig pkgconf +pkg install cmake freetype2 fontconfig pkgconf python3 ``` #### OpenBSD @@ -248,6 +251,16 @@ make app cp -r target/release/osx/Alacritty.app /Applications/ ``` +#### Universal Binary + +The following will build an executable that runs on both x86 and ARM macos +architectures: + +```sh +rustup target add x86_64-apple-darwin aarch64-apple-darwin +make app-universal +``` + ## Post Build There are some extra things you might want to set up after installing Alacritty. @@ -26,15 +26,21 @@ vpath $(DMG_NAME) $(APP_DIR) all: help -help: ## Prints help for targets with comments +help: ## Print this help message @grep -E '^[a-zA-Z._-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' -binary: | $(TARGET) ## Build release binary with cargo -$(TARGET): +binary: $(TAGET)-native ## Build a release binary +binary-universal: $(TAGET)-universal ## Build a universal release binary +$(TARGET)-native: MACOSX_DEPLOYMENT_TARGET="10.11" cargo build --release +$(TARGET)-universal: + MACOSX_DEPLOYMENT_TARGET="10.11" cargo build --release --target=x86_64-apple-darwin + MACOSX_DEPLOYMENT_TARGET="10.11" cargo build --release --target=aarch64-apple-darwin + @lipo target/{x86_64,aarch64}-apple-darwin/release/$(TARGET) -create -output $(APP_BINARY) -app: | $(APP_NAME) ## Clone Alacritty.app template and mount binary -$(APP_NAME): $(TARGET) +app: $(APP_NAME)-native ## Create an Alacritty.app +app-universal: $(APP_NAME)-universal ## Create a universal Alacritty.app +$(APP_NAME)-%: $(TARGET)-% @mkdir -p $(APP_BINARY_DIR) @mkdir -p $(APP_EXTRAS_DIR) @mkdir -p $(APP_COMPLETIONS_DIR) @@ -44,10 +50,11 @@ $(APP_NAME): $(TARGET) @cp -fp $(APP_BINARY) $(APP_BINARY_DIR) @cp -fp $(COMPLETIONS) $(APP_COMPLETIONS_DIR) @touch -r "$(APP_BINARY)" "$(APP_DIR)/$(APP_NAME)" - @echo "Created '$@' in '$(APP_DIR)'" + @echo "Created '$(APP_NAME)' in '$(APP_DIR)'" -dmg: | $(DMG_NAME) ## Pack Alacritty.app into .dmg -$(DMG_NAME): $(APP_NAME) +dmg: $(DMG_NAME)-native ## Create an Alacritty.dmg +dmg-universal: $(DMG_NAME)-universal ## Create a universal Alacritty.dmg +$(DMG_NAME)-%: $(APP_NAME)-% @echo "Packing disk image..." @ln -sf /Applications $(DMG_DIR)/Applications @hdiutil create $(DMG_DIR)/$(DMG_NAME) \ @@ -55,12 +62,14 @@ $(DMG_NAME): $(APP_NAME) -fs HFS+ \ -srcfolder $(APP_DIR) \ -ov -format UDZO - @echo "Packed '$@' in '$(APP_DIR)'" + @echo "Packed '$(APP_NAME)' in '$(APP_DIR)'" -install: $(DMG_NAME) ## Mount disk image +install: $(INSTALL)-native ## Mount disk image +install-universal: $(INSTALL)-native ## Mount universal disk image +$(INSTALL)-%: $(DMG_NAME)-% @open $(DMG_DIR)/$(DMG_NAME) -.PHONY: app binary clean dmg install $(TARGET) +.PHONY: app binary clean dmg install $(TARGET) $(TARGET)-universal -clean: ## Remove all artifacts - -rm -rf $(APP_DIR) +clean: ## Remove all build artifacts + @cargo clean diff --git a/alacritty.yml b/alacritty.yml index 21f06fd0..04654e56 100644 --- a/alacritty.yml +++ b/alacritty.yml @@ -63,6 +63,12 @@ # - buttonless: Title bar, transparent background and no title bar buttons #decorations: full + # Background opacity + # + # Window opacity as a floating point number from `0.0` to `1.0`. + # The value `0.0` is completely transparent and `1.0` is opaque. + #opacity: 1.0 + # Startup Mode (changes require restart) # # Values for `startup_mode`: @@ -312,6 +318,13 @@ # #indexed_colors: [] + # Transparent cell backgrounds + # + # Whether or not `window.opacity` applies to all cell backgrounds or only to + # the default background. When set to `true` all cells will be transparent + # regardless of their background color. + #transparent_background_colors: false + # Bell # # The bell is rung every time the BEL control character is received. @@ -353,12 +366,6 @@ # #command: None -# Background opacity -# -# Window opacity as a floating point number from `0.0` to `1.0`. -# The value `0.0` is completely transparent and `1.0` is opaque. -#background_opacity: 1.0 - #selection: # This string contains all characters that are used as separators for # "semantic words" in Alacritty. @@ -458,7 +465,7 @@ # Each hint must have a `regex` and either an `action` or a `command` field. # The fields `mouse`, `binding` and `post_processing` are optional. # - # The fields `command`, `binding.key`, `binding.mods`, `binding.mode` and + # The fields `command`, `binding.key`, `binding.mods`, `binding.mode` and # `mouse.mods` accept the same values as they do in the `key_bindings` section. # # The `mouse.enabled` field controls if the hint should be underlined while @@ -507,13 +514,19 @@ # - Right # - Numeric identifier such as `5` # -# - `action` (see key bindings) +# - `action` (see key bindings for actions not exclusive to mouse mode) +# +# - Mouse exclusive actions: +# +# - ExpandSelection +# Expand the selection to the current mouse cursor location. # # And optionally: # # - `mods` (see key bindings) #mouse_bindings: -# - { mouse: Middle, action: PasteSelection } +# - { mouse: Right, action: ExpandSelection } +# - { mouse: Middle, mode: ~Vi, action: PasteSelection } # Key bindings # @@ -730,11 +743,11 @@ #- { key: End, mods: Shift, mode: ~Alt, action: ScrollToBottom } # Vi Mode - #- { key: Space, mods: Shift|Control, mode: Vi|~Search, action: ScrollToBottom } #- { key: Space, mods: Shift|Control, mode: ~Search, action: ToggleViMode } + #- { key: Space, mods: Shift|Control, mode: Vi|~Search, action: ScrollToBottom } #- { key: Escape, mode: Vi|~Search, action: ClearSelection } - #- { key: I, mode: Vi|~Search, action: ScrollToBottom } #- { key: I, mode: Vi|~Search, action: ToggleViMode } + #- { key: I, mode: Vi|~Search, action: ScrollToBottom } #- { key: C, mods: Control, mode: Vi|~Search, action: ToggleViMode } #- { key: Y, mods: Control, mode: Vi|~Search, action: ScrollLineUp } #- { key: E, mods: Control, mode: Vi|~Search, action: ScrollLineDown } diff --git a/alacritty/Cargo.toml b/alacritty/Cargo.toml index 99e61f56..74366c43 100644 --- a/alacritty/Cargo.toml +++ b/alacritty/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "alacritty" -version = "0.9.0-dev" +version = "0.10.0-dev" authors = ["Christian Duerr <contact@christianduerr.com>", "Joe Wilm <joe@jwilm.com>"] license = "Apache-2.0" description = "A fast, cross-platform, OpenGL terminal emulator" @@ -10,7 +10,7 @@ edition = "2018" [dependencies.alacritty_terminal] path = "../alacritty_terminal" -version = "0.13.1-dev" +version = "0.15.1-dev" default-features = false [dependencies.alacritty_config_derive] @@ -18,7 +18,7 @@ path = "../alacritty_config_derive" version = "0.1.0" [dependencies] -clap = "2" +structopt = "0.3.22" log = { version = "0.4", features = ["std", "serde"] } time = "0.1.40" fnv = "1" @@ -28,13 +28,13 @@ serde_json = "1" glutin = { version = "0.26.0", default-features = false, features = ["serde"] } notify = "4" parking_lot = "0.11.0" -crossfont = { version = "0.3.0", features = ["force_system_fontconfig"] } +crossfont = { version = "0.3.1", features = ["force_system_fontconfig"] } copypasta = { version = "0.7.0", default-features = false } libc = "0.2" unicode-width = "0.1" bitflags = "1" dirs = "3.0.1" -memoffset = "0.6.1" +memoffset = "0.6.4" [build-dependencies] gl_generator = "0.14.0" diff --git a/alacritty/build.rs b/alacritty/build.rs index 48841ca8..e81da150 100644 --- a/alacritty/build.rs +++ b/alacritty/build.rs @@ -6,7 +6,11 @@ use std::process::Command; use gl_generator::{Api, Fallbacks, GlobalGenerator, Profile, Registry}; fn main() { - println!("cargo:rustc-env=GIT_HASH={}", commit_hash()); + let mut version = String::from(env!("CARGO_PKG_VERSION")); + if let Some(commit_hash) = commit_hash() { + version = format!("{} ({})", version, commit_hash); + } + println!("cargo:rustc-env=VERSION={}", version); let dest = env::var("OUT_DIR").unwrap(); let mut file = File::create(&Path::new(&dest).join("gl_bindings.rs")).unwrap(); @@ -16,14 +20,14 @@ fn main() { .unwrap(); #[cfg(windows)] - embed_resource::compile("../extra/windows/windows.rc"); + embed_resource::compile("./windows/windows.rc"); } -fn commit_hash() -> String { +fn commit_hash() -> Option<String> { Command::new("git") .args(&["rev-parse", "--short", "HEAD"]) .output() .ok() .and_then(|output| String::from_utf8(output.stdout).ok()) - .unwrap_or_default() + .map(|hash| hash.trim().into()) } diff --git a/alacritty/res/text.f.glsl b/alacritty/res/text.f.glsl index d5e26881..5b4e3da6 100644 --- a/alacritty/res/text.f.glsl +++ b/alacritty/res/text.f.glsl @@ -18,7 +18,7 @@ void main() { } alphaMask = vec4(1.0); - color = vec4(bg.rgb, 1.0); + color = vec4(bg.rgb, bg.a); } else if ((int(fg.a) & COLORED) != 0) { // Color glyphs, like emojis. vec4 glyphColor = texture(mask, TexCoords); diff --git a/alacritty/res/text.v.glsl b/alacritty/res/text.v.glsl index 31e6f934..a4a31382 100644 --- a/alacritty/res/text.v.glsl +++ b/alacritty/res/text.v.glsl @@ -65,6 +65,6 @@ void main() { TexCoords = uvOffset + position * uvSize; } - bg = vec4(backgroundColor.rgb / 255.0, backgroundColor.a); + bg = backgroundColor / 255.0; fg = vec4(textColor.rgb / 255.0, textColor.a); } diff --git a/alacritty/src/cli.rs b/alacritty/src/cli.rs index 682bdde3..a1480807 100644 --- a/alacritty/src/cli.rs +++ b/alacritty/src/cli.rs @@ -1,219 +1,101 @@ use std::cmp::max; use std::path::PathBuf; -use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; use log::{self, error, LevelFilter}; use serde_yaml::Value; +use structopt::StructOpt; use alacritty_terminal::config::Program; use crate::config::serde_utils; -use crate::config::window::DEFAULT_NAME; +use crate::config::window::{Class, DEFAULT_NAME}; use crate::config::Config; -#[cfg(not(any(target_os = "macos", windows)))] -const CONFIG_PATH: &str = "$XDG_CONFIG_HOME/alacritty/alacritty.yml"; -#[cfg(windows)] -const CONFIG_PATH: &str = "%APPDATA%\\alacritty\\alacritty.yml"; -#[cfg(target_os = "macos")] -const CONFIG_PATH: &str = "$HOME/.config/alacritty/alacritty.yml"; - /// Options specified on the command line. +#[derive(StructOpt, Debug)] +#[structopt(author, about, version = env!("VERSION"))] pub struct Options { + /// Print all events to stdout. + #[structopt(long)] pub print_events: bool, + + /// Generates ref test. + #[structopt(long)] pub ref_test: bool, + + /// Defines the window title [default: Alacritty]. + #[structopt(short, long)] pub title: Option<String>, - pub class_instance: Option<String>, - pub class_general: Option<String>, - pub embed: Option<String>, - pub log_level: LevelFilter, - pub command: Option<Program>, - pub hold: bool, - pub working_directory: Option<PathBuf>, - pub config_path: Option<PathBuf>, - pub config_options: Value, -} -impl Default for Options { - fn default() -> Options { - Options { - print_events: false, - ref_test: false, - title: None, - class_instance: None, - class_general: None, - embed: None, - log_level: LevelFilter::Warn, - command: None, - hold: false, - working_directory: None, - config_path: None, - config_options: Value::Null, - } - } -} + /// Defines window class/app_id on X11/Wayland [default: Alacritty]. + #[structopt(long, value_name = "instance> | <instance>,<general", parse(try_from_str = parse_class))] + pub class: Option<Class>, -impl Options { - /// Build `Options` from command line arguments. - pub fn new() -> Self { - let mut version = crate_version!().to_owned(); - let commit_hash = env!("GIT_HASH"); - if !commit_hash.is_empty() { - version = format!("{} ({})", version, commit_hash); - } + /// Defines the X11 window ID (as a decimal integer) to embed Alacritty within. + #[structopt(long)] + pub embed: Option<String>, - let mut options = Options::default(); - - let matches = App::new(crate_name!()) - .version(version.as_str()) - .author(crate_authors!("\n")) - .about(crate_description!()) - .arg(Arg::with_name("ref-test").long("ref-test").help("Generates ref test")) - .arg( - Arg::with_name("print-events") - .long("print-events") - .help("Print all events to stdout"), - ) - .arg( - Arg::with_name("title") - .long("title") - .short("t") - .takes_value(true) - .help(&format!("Defines the window title [default: {}]", DEFAULT_NAME)), - ) - .arg( - Arg::with_name("class") - .long("class") - .value_name("instance> | <instance>,<general") - .takes_value(true) - .use_delimiter(true) - .help(&format!( - "Defines window class/app_id on X11/Wayland [default: {}]", - DEFAULT_NAME - )), - ) - .arg( - Arg::with_name("embed").long("embed").takes_value(true).help( - "Defines the X11 window ID (as a decimal integer) to embed Alacritty within", - ), - ) - .arg( - Arg::with_name("q") - .short("q") - .multiple(true) - .conflicts_with("v") - .help("Reduces the level of verbosity (the min level is -qq)"), - ) - .arg( - Arg::with_name("v") - .short("v") - .multiple(true) - .conflicts_with("q") - .help("Increases the level of verbosity (the max level is -vvv)"), - ) - .arg( - Arg::with_name("working-directory") - .long("working-directory") - .takes_value(true) - .help("Start the shell in the specified working directory"), - ) - .arg(Arg::with_name("config-file").long("config-file").takes_value(true).help( - &format!("Specify alternative configuration file [default: {}]", CONFIG_PATH), - )) - .arg( - Arg::with_name("command") - .long("command") - .short("e") - .multiple(true) - .takes_value(true) - .allow_hyphen_values(true) - .help("Command and args to execute (must be last argument)"), - ) - .arg(Arg::with_name("hold").long("hold").help("Remain open after child process exits")) - .arg( - Arg::with_name("option") - .long("option") - .short("o") - .multiple(true) - .takes_value(true) - .help("Override configuration file options [example: cursor.style=Beam]"), - ) - .get_matches(); - - if matches.is_present("ref-test") { - options.ref_test = true; - } + /// Start the shell in the specified working directory. + #[structopt(long)] + pub working_directory: Option<PathBuf>, - if matches.is_present("print-events") { - options.print_events = true; - } + /// Specify alternative configuration file [default: $XDG_CONFIG_HOME/alacritty/alacritty.yml]. + #[cfg(not(any(target_os = "macos", windows)))] + #[structopt(long)] + pub config_file: Option<PathBuf>, - if let Some(mut class) = matches.values_of("class") { - options.class_instance = class.next().map(|instance| instance.to_owned()); - options.class_general = class.next().map(|general| general.to_owned()); - } + /// Specify alternative configuration file [default: %APPDATA%\alacritty\alacritty.yml]. + #[cfg(windows)] + #[structopt(long)] + pub config_file: Option<PathBuf>, - options.title = matches.value_of("title").map(ToOwned::to_owned); - options.embed = matches.value_of("embed").map(ToOwned::to_owned); + /// Specify alternative configuration file [default: $HOME/.config/alacritty/alacritty.yml]. + #[cfg(target_os = "macos")] + #[structopt(long)] + pub config_file: Option<PathBuf>, - match matches.occurrences_of("q") { - 0 => (), - 1 => options.log_level = LevelFilter::Error, - _ => options.log_level = LevelFilter::Off, - } + /// Remain open after child process exits. + #[structopt(long)] + pub hold: bool, - match matches.occurrences_of("v") { - 0 if !options.print_events => options.log_level = LevelFilter::Warn, - 0 | 1 => options.log_level = LevelFilter::Info, - 2 => options.log_level = LevelFilter::Debug, - _ => options.log_level = LevelFilter::Trace, - } + /// CLI options for config overrides. + #[structopt(skip)] + pub config_options: Value, - if let Some(dir) = matches.value_of("working-directory") { - options.working_directory = Some(PathBuf::from(dir.to_string())); - } + /// Reduces the level of verbosity (the min level is -qq). + #[structopt(short, conflicts_with("verbose"), parse(from_occurrences))] + quiet: u8, - if let Some(path) = matches.value_of("config-file") { - options.config_path = Some(PathBuf::from(path.to_string())); - } + /// Increases the level of verbosity (the max level is -vvv). + #[structopt(short, conflicts_with("quiet"), parse(from_occurrences))] + verbose: u8, - if let Some(mut args) = matches.values_of("command") { - // The following unwrap is guaranteed to succeed. - // If `command` exists it must also have a first item since - // `Arg::min_values(1)` is set. - let program = String::from(args.next().unwrap()); - let args = args.map(String::from).collect(); - options.command = Some(Program::WithArgs { program, args }); - } + /// Command and args to execute (must be last argument). + #[structopt(short = "e", long, allow_hyphen_values = true)] + command: Vec<String>, - if matches.is_present("hold") { - options.hold = true; - } + /// Override configuration file options [example: cursor.style=Beam]. + #[structopt(short = "o", long)] + option: Vec<String>, +} - if let Some(config_options) = matches.values_of("option") { - for option in config_options { - match option_as_value(option) { - Ok(value) => { - options.config_options = serde_utils::merge(options.config_options, value); - }, - Err(_) => eprintln!("Invalid CLI config option: {:?}", option), - } +impl Options { + pub fn new() -> Self { + let mut options = Self::from_args(); + + // Convert `--option` flags into serde `Value`. + for option in &options.option { + match option_as_value(option) { + Ok(value) => { + options.config_options = serde_utils::merge(options.config_options, value); + }, + Err(_) => eprintln!("Invalid CLI config option: {:?}", option), } } options } - /// Configuration file path. - pub fn config_path(&self) -> Option<PathBuf> { - self.config_path.clone() - } - - /// CLI config options as deserializable serde value. - pub fn config_options(&self) -> &Value { - &self.config_options - } - /// Override configuration file with options from the CLI. pub fn override_config(&self, config: &mut Config) { if let Some(working_directory) = &self.working_directory { @@ -224,8 +106,8 @@ impl Options { } } - if let Some(command) = &self.command { - config.shell = Some(command.clone()); + if let Some(command) = self.command() { + config.shell = Some(command); } config.hold = self.hold; @@ -233,17 +115,14 @@ impl Options { if let Some(title) = self.title.clone() { config.ui_config.window.title = title } - if let Some(class_instance) = self.class_instance.clone() { - config.ui_config.window.class.instance = class_instance; - } - if let Some(class_general) = self.class_general.clone() { - config.ui_config.window.class.general = class_general; + if let Some(class) = &self.class { + config.ui_config.window.class = class.clone(); } config.ui_config.window.dynamic_title &= self.title.is_none(); config.ui_config.window.embed = self.embed.as_ref().and_then(|embed| embed.parse().ok()); config.ui_config.debug.print_events |= self.print_events; - config.ui_config.debug.log_level = max(config.ui_config.debug.log_level, self.log_level); + config.ui_config.debug.log_level = max(config.ui_config.debug.log_level, self.log_level()); config.ui_config.debug.ref_test |= self.ref_test; if config.ui_config.debug.print_events { @@ -251,6 +130,32 @@ impl Options { max(config.ui_config.debug.log_level, LevelFilter::Info); } } + + /// Logging filter level. + pub fn log_level(&self) -> LevelFilter { + match (self.quiet, self.verbose) { + // Force at least `Info` level for `--print-events`. + (_, 0) if self.print_events => LevelFilter::Info, + + // Default. + (0, 0) => LevelFilter::Warn, + + // Verbose. + (_, 1) => LevelFilter::Info, + (_, 2) => LevelFilter::Debug, + (0, _) => LevelFilter::Trace, + + // Quiet. + (1, _) => LevelFilter::Error, + (..) => LevelFilter::Off, + } + } + + /// Shell override passed through the CLI. + pub fn command(&self) -> Option<Program> { + let (program, args) = self.command.split_first()?; + Some(Program::WithArgs { program: program.clone(), args: args.to_vec() }) + } } /// Format an option in the format of `parent.field=value` to a serde Value. @@ -278,6 +183,23 @@ fn option_as_value(option: &str) -> Result<Value, serde_yaml::Error> { serde_yaml::from_str(&yaml_text) } +/// Parse the class CLI parameter. +fn parse_class(input: &str) -> Result<Class, String> { + match input.find(',') { + Some(position) => { + let general = input[position + 1..].to_owned(); + + // Warn the user if they've passed too many values. + if general.contains(',') { + return Err(String::from("Too many parameters")); + } + + Ok(Class { instance: input[..position].into(), general }) + }, + None => Ok(Class { instance: input.into(), general: DEFAULT_NAME.into() }), + } +} + #[cfg(test)] mod tests { use super::*; @@ -289,7 +211,7 @@ mod tests { let mut config = Config::default(); let old_dynamic_title = config.ui_config.window.dynamic_title; - Options::default().override_config(&mut config); + Options::new().override_config(&mut config); assert_eq!(old_dynamic_title, config.ui_config.window.dynamic_title); } @@ -298,7 +220,7 @@ mod tests { fn dynamic_title_overridden_by_options() { let mut config = Config::default(); - let options = Options { title: Some("foo".to_owned()), ..Options::default() }; + let options = Options { title: Some("foo".to_owned()), ..Options::new() }; options.override_config(&mut config); assert!(!config.ui_config.window.dynamic_title); @@ -309,7 +231,7 @@ mod tests { let mut config = Config::default(); config.ui_config.window.title = "foo".to_owned(); - Options::default().override_config(&mut config); + Options::new().override_config(&mut config); assert!(config.ui_config.window.dynamic_title); } @@ -350,4 +272,24 @@ mod tests { assert_eq!(value, Value::Mapping(expected)); } + + #[test] + fn parse_instance_class() { + let class = parse_class("one").unwrap(); + assert_eq!(class.instance, "one"); + assert_eq!(class.general, DEFAULT_NAME); + } + + #[test] + fn parse_general_class() { + let class = parse_class("one,two").unwrap(); + assert_eq!(class.instance, "one"); + assert_eq!(class.general, "two"); + } + + #[test] + fn parse_invalid_class() { + let class = parse_class("one,two,three"); + assert!(class.is_err()); + } } diff --git a/alacritty/src/config/bindings.rs b/alacritty/src/config/bindings.rs index 12349639..a4271430 100644 --- a/alacritty/src/config/bindings.rs +++ b/alacritty/src/config/bindings.rs @@ -103,11 +103,15 @@ pub enum Action { /// Perform vi mode action. #[config(skip)] - ViAction(ViAction), + Vi(ViAction), /// Perform search mode action. #[config(skip)] - SearchAction(SearchAction), + Search(SearchAction), + + /// Perform mouse binding exclusive action. + #[config(skip)] + Mouse(MouseAction), /// Paste contents of system clipboard. Paste, @@ -211,7 +215,7 @@ impl From<&'static str> for Action { impl From<ViAction> for Action { fn from(action: ViAction) -> Self { - Self::ViAction(action) + Self::Vi(action) } } @@ -223,7 +227,13 @@ impl From<ViMotion> for Action { impl From<SearchAction> for Action { fn from(action: SearchAction) -> Self { - Self::SearchAction(action) + Self::Search(action) + } +} + +impl From<MouseAction> for Action { + fn from(action: MouseAction) -> Self { + Self::Mouse(action) } } @@ -232,7 +242,8 @@ impl Display for Action { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Action::ViMotion(motion) => motion.fmt(f), - Action::ViAction(action) => action.fmt(f), + Action::Vi(action) => action.fmt(f), + Action::Mouse(action) => action.fmt(f), _ => write!(f, "{:?}", self), } } @@ -262,6 +273,7 @@ pub enum ViAction { } /// Search mode specific actions. +#[allow(clippy::enum_variant_names)] #[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)] pub enum SearchAction { /// Move the focus to the next search match. @@ -282,6 +294,13 @@ pub enum SearchAction { SearchHistoryNext, } +/// Mouse binding specific actions. +#[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)] +pub enum MouseAction { + /// Expand the selection to the current mouse cursor position. + ExpandSelection, +} + macro_rules! bindings { ( KeyBinding; @@ -342,6 +361,7 @@ macro_rules! bindings { pub fn default_mouse_bindings() -> Vec<MouseBinding> { bindings!( MouseBinding; + MouseButton::Right; MouseAction::ExpandSelection; MouseButton::Middle, ~BindingMode::VI; Action::PasteSelection; ) } @@ -423,16 +443,16 @@ pub fn default_key_bindings() -> Vec<KeyBinding> { F19, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[33~".into()); F20, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[34~".into()); NumpadEnter, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\n".into()); - Space, ModifiersState::SHIFT | ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH; - Action::ScrollToBottom; Space, ModifiersState::SHIFT | ModifiersState::CTRL, ~BindingMode::SEARCH; Action::ToggleViMode; + Space, ModifiersState::SHIFT | ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH; + Action::ScrollToBottom; Escape, +BindingMode::VI, ~BindingMode::SEARCH; Action::ClearSelection; I, +BindingMode::VI, ~BindingMode::SEARCH; - Action::ScrollToBottom; - I, +BindingMode::VI, ~BindingMode::SEARCH; Action::ToggleViMode; + I, +BindingMode::VI, ~BindingMode::SEARCH; + Action::ScrollToBottom; C, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH; Action::ToggleViMode; Y, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH; @@ -1026,6 +1046,9 @@ impl<'a> Deserialize<'a> for RawBinding { SearchAction::deserialize(value.clone()) { Some(search_action.into()) + } else if let Ok(mouse_action) = MouseAction::deserialize(value.clone()) + { + Some(mouse_action.into()) } else { match Action::deserialize(value.clone()).map_err(V::Error::custom) { Ok(action) => Some(action), @@ -1081,7 +1104,7 @@ impl<'a> Deserialize<'a> for RawBinding { let action = match (action, chars, command) { (Some(action @ Action::ViMotion(_)), None, None) - | (Some(action @ Action::ViAction(_)), None, None) => { + | (Some(action @ Action::Vi(_)), None, None) => { if !mode.intersects(BindingMode::VI) || not_mode.intersects(BindingMode::VI) { return Err(V::Error::custom(format!( @@ -1091,7 +1114,7 @@ impl<'a> Deserialize<'a> for RawBinding { } action }, - (Some(action @ Action::SearchAction(_)), None, None) => { + (Some(action @ Action::Search(_)), None, None) => { if !mode.intersects(BindingMode::SEARCH) { return Err(V::Error::custom(format!( "action `{}` is only available in search mode, try adding `mode: \ @@ -1101,6 +1124,15 @@ impl<'a> Deserialize<'a> for RawBinding { } action }, + (Some(action @ Action::Mouse(_)), None, None) => { + if mouse.is_none() { + return Err(V::Error::custom(format!( + "action `{}` is only available for mouse bindings", + action, + ))); + } + action + }, (Some(action), None, None) => action, (None, Some(chars), None) => Action::Esc(chars), (None, None, Some(cmd)) => Action::Command(cmd), diff --git a/alacritty/src/config/color.rs b/alacritty/src/config/color.rs index ddb1da29..c0076edb 100644 --- a/alacritty/src/config/color.rs +++ b/alacritty/src/config/color.rs @@ -17,6 +17,7 @@ pub struct Colors { pub search: SearchColors, pub line_indicator: LineIndicatorColors, pub hints: HintColors, + pub transparent_background_colors: bool, } impl Colors { diff --git a/alacritty/src/config/mod.rs b/alacritty/src/config/mod.rs index e2476bb2..4a3c0ae9 100644 --- a/alacritty/src/config/mod.rs +++ b/alacritty/src/config/mod.rs @@ -22,7 +22,9 @@ mod bindings; mod mouse; use crate::cli::Options; -pub use crate::config::bindings::{Action, Binding, BindingMode, Key, SearchAction, ViAction}; +pub use crate::config::bindings::{ + Action, Binding, BindingMode, Key, MouseAction, SearchAction, ViAction, +}; #[cfg(test)] pub use crate::config::mouse::{ClickHandler, Mouse}; use crate::config::ui_config::UiConfig; @@ -99,8 +101,8 @@ impl From<serde_yaml::Error> for Error { /// Load the configuration file. pub fn load(options: &Options) -> Config { - let config_options = options.config_options().clone(); - let config_path = options.config_path().or_else(installed_config); + let config_options = options.config_options.clone(); + let config_path = options.config_file.clone().or_else(installed_config); // Load the config using the following fallback behavior: // - Config path + CLI overrides @@ -126,7 +128,7 @@ pub fn load(options: &Options) -> Config { /// Attempt to reload the configuration file. pub fn reload(config_path: &Path, options: &Options) -> Result<Config> { // Load config, propagating errors. - let config_options = options.config_options().clone(); + let config_options = options.config_options.clone(); let mut config = load_from(config_path, config_options)?; after_loading(&mut config, options); @@ -157,7 +159,7 @@ fn load_from(path: &Path, cli_config: Value) -> Result<Config> { /// Deserialize configuration file from path. fn read_config(path: &Path, cli_config: Value) -> Result<Config> { let mut config_paths = Vec::new(); - let mut config_value = parse_config(&path, &mut config_paths, IMPORT_RECURSION_LIMIT)?; + let mut config_value = parse_config(path, &mut config_paths, IMPORT_RECURSION_LIMIT)?; // Override config with CLI options. config_value = serde_utils::merge(config_value, cli_config); diff --git a/alacritty/src/config/monitor.rs b/alacritty/src/config/monitor.rs index 4a694fac..e3dd0556 100644 --- a/alacritty/src/config/monitor.rs +++ b/alacritty/src/config/monitor.rs @@ -1,4 +1,3 @@ -use std::fs; use std::path::PathBuf; use std::sync::mpsc; use std::time::Duration; @@ -16,23 +15,21 @@ const DEBOUNCE_DELAY: Duration = Duration::from_millis(10); const DEBOUNCE_DELAY: Duration = Duration::from_millis(1000); pub fn watch(mut paths: Vec<PathBuf>, event_proxy: EventProxy) { - // Canonicalize all paths, filtering out the ones that do not exist. - paths = paths - .drain(..) - .filter_map(|path| match fs::canonicalize(&path) { - Ok(path) => Some(path), - Err(err) => { - error!("Unable to canonicalize config path {:?}: {}", path, err); - None - }, - }) - .collect(); - // Don't monitor config if there is no path to watch. if paths.is_empty() { return; } + // Canonicalize paths, keeping the base paths for symlinks. + for i in 0..paths.len() { + if let Ok(canonical_path) = paths[i].canonicalize() { + match paths[i].symlink_metadata() { + Ok(metadata) if metadata.file_type().is_symlink() => paths.push(canonical_path), + _ => paths[i] = canonical_path, + } + } + } + // The Duration argument is a debouncing period. let (tx, rx) = mpsc::channel(); let mut watcher = match watcher(tx, DEBOUNCE_DELAY) { @@ -73,17 +70,15 @@ pub fn watch(mut paths: Vec<PathBuf>, event_proxy: EventProxy) { }; match event { - DebouncedEvent::Rename(..) => continue, - DebouncedEvent::Write(path) + DebouncedEvent::Rename(_, path) + | DebouncedEvent::Write(path) | DebouncedEvent::Create(path) - | DebouncedEvent::Chmod(path) => { - if !paths.contains(&path) { - continue; - } - + | DebouncedEvent::Chmod(path) + if paths.contains(&path) => + { // Always reload the primary configuration file. event_proxy.send_event(Event::ConfigReload(paths[0].clone())); - }, + } _ => {}, } } diff --git a/alacritty/src/config/ui_config.rs b/alacritty/src/config/ui_config.rs index a58c1fd3..3ce02161 100644 --- a/alacritty/src/config/ui_config.rs +++ b/alacritty/src/config/ui_config.rs @@ -69,7 +69,8 @@ pub struct UiConfig { mouse_bindings: MouseBindings, /// Background opacity from 0.0 to 1.0. - background_opacity: Percentage, + #[config(deprecated = "use window.opacity instead")] + background_opacity: Option<Percentage>, } impl Default for UiConfig { @@ -115,13 +116,13 @@ impl UiConfig { } #[inline] - pub fn background_opacity(&self) -> f32 { - self.background_opacity.as_f32() + pub fn window_opacity(&self) -> f32 { + self.background_opacity.unwrap_or(self.window.opacity).as_f32() } #[inline] pub fn key_bindings(&self) -> &[KeyBinding] { - &self.key_bindings.0.as_slice() + self.key_bindings.0.as_slice() } #[inline] @@ -399,7 +400,7 @@ impl LazyRegexVariant { }; // Compile the regex. - let regex_search = match RegexSearch::new(®ex) { + let regex_search = match RegexSearch::new(regex) { Ok(regex_search) => regex_search, Err(error) => { error!("hint regex is invalid: {}", error); diff --git a/alacritty/src/config/window.rs b/alacritty/src/config/window.rs index d74390d8..f7a7511c 100644 --- a/alacritty/src/config/window.rs +++ b/alacritty/src/config/window.rs @@ -7,7 +7,7 @@ use serde::de::{self, MapAccess, Visitor}; use serde::{Deserialize, Deserializer}; use alacritty_config_derive::ConfigDeserialize; -use alacritty_terminal::config::LOG_TARGET_CONFIG; +use alacritty_terminal::config::{Percentage, LOG_TARGET_CONFIG}; use alacritty_terminal::index::Column; use crate::config::ui_config::Delta; @@ -15,7 +15,7 @@ use crate::config::ui_config::Delta; /// Default Alacritty name, used for window title and class. pub const DEFAULT_NAME: &str = "Alacritty"; -#[derive(ConfigDeserialize, Debug, Clone, PartialEq, Eq)] +#[derive(ConfigDeserialize, Debug, Clone, PartialEq)] pub struct WindowConfig { /// Initial position. pub position: Option<Delta<i32>>, @@ -45,6 +45,9 @@ pub struct WindowConfig { /// Window class. pub class: Class, + /// Background opacity from 0.0 to 1.0. + pub opacity: Percentage, + /// Pixel padding. padding: Delta<u8>, @@ -64,6 +67,7 @@ impl Default for WindowConfig { gtk_theme_variant: Default::default(), dynamic_padding: Default::default(), class: Default::default(), + opacity: Default::default(), padding: Default::default(), dimensions: Default::default(), } diff --git a/alacritty/src/display/content.rs b/alacritty/src/display/content.rs index b470343e..0bc2f46f 100644 --- a/alacritty/src/display/content.rs +++ b/alacritty/src/display/content.rs @@ -45,7 +45,7 @@ impl<'a> RenderableContent<'a> { term: &'a Term<T>, search_state: &'a SearchState, ) -> Self { - let search = search_state.dfas().map(|dfas| Regex::new(&term, dfas)); + let search = search_state.dfas().map(|dfas| Regex::new(term, dfas)); let focused_match = search_state.focused_match(); let terminal_content = term.renderable_content(); @@ -205,7 +205,7 @@ impl RenderableCell { mem::swap(&mut fg, &mut bg); 1.0 } else { - Self::compute_bg_alpha(cell.bg) + Self::compute_bg_alpha(&content.config.ui_config, cell.bg) }; let is_selected = content.terminal_content.selection.map_or(false, |selection| { @@ -353,9 +353,11 @@ impl RenderableCell { /// using the named input color, rather than checking the RGB of the background after its color /// is computed. #[inline] - fn compute_bg_alpha(bg: Color) -> f32 { + fn compute_bg_alpha(config: &UiConfig, bg: Color) -> f32 { if bg == Color::Named(NamedColor::Background) { 0. + } else if config.colors.transparent_background_colors { + config.window_opacity() } else { 1. } diff --git a/alacritty/src/display/mod.rs b/alacritty/src/display/mod.rs index 162c70b9..5e958f03 100644 --- a/alacritty/src/display/mod.rs +++ b/alacritty/src/display/mod.rs @@ -80,7 +80,7 @@ pub enum Error { Render(renderer::Error), /// Error during buffer swap. - ContextError(glutin::ContextError), + Context(glutin::ContextError), } impl std::error::Error for Error { @@ -89,7 +89,7 @@ impl std::error::Error for Error { Error::Window(err) => err.source(), Error::Font(err) => err.source(), Error::Render(err) => err.source(), - Error::ContextError(err) => err.source(), + Error::Context(err) => err.source(), } } } @@ -100,7 +100,7 @@ impl fmt::Display for Error { Error::Window(err) => err.fmt(f), Error::Font(err) => err.fmt(f), Error::Render(err) => err.fmt(f), - Error::ContextError(err) => err.fmt(f), + Error::Context(err) => err.fmt(f), } } } @@ -125,7 +125,7 @@ impl From<renderer::Error> for Error { impl From<glutin::ContextError> for Error { fn from(val: glutin::ContextError) -> Self { - Error::ContextError(val) + Error::Context(val) } } @@ -242,7 +242,7 @@ impl Display { // Spawn the Alacritty window. let mut window = Window::new( event_loop, - &config, + config, estimated_size, #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] wayland_event_queue.as_ref(), @@ -299,7 +299,7 @@ impl Display { // Disable shadows for transparent windows on macOS. #[cfg(target_os = "macos")] - window.set_has_shadow(config.ui_config.background_opacity() >= 1.0); + window.set_has_shadow(config.ui_config.window_opacity() >= 1.0); // On Wayland we can safely ignore this call, since the window isn't visible until you // actually draw something into it and commit those changes. @@ -487,7 +487,7 @@ impl Display { // Collect renderable content before the terminal is dropped. let mut content = RenderableContent::new(config, self, &terminal, search_state); let mut grid_cells = Vec::new(); - while let Some(cell) = content.next() { + for cell in &mut content { grid_cells.push(cell); } let background_color = content.color(NamedColor::Background as usize); @@ -611,7 +611,7 @@ impl Display { for (i, message_text) in text.iter().enumerate() { let point = Point::new(start_line + i, Column(0)); self.renderer.with_api(&config.ui_config, &size_info, |mut api| { - api.render_string(glyph_cache, point, fg, bg, &message_text); + api.render_string(glyph_cache, point, fg, bg, message_text); }); } } else { @@ -682,7 +682,7 @@ impl Display { let vi_highlighted_hint = if term.mode().contains(TermMode::VI) { let mods = ModifiersState::all(); let point = term.vi_mode_cursor.point; - hint::highlighted_at(&term, config, point, mods) + hint::highlighted_at(term, config, point, mods) } else { None }; @@ -698,7 +698,7 @@ impl Display { // Find highlighted hint at mouse position. let point = mouse.point(&self.size_info, term.grid().display_offset()); - let highlighted_hint = hint::highlighted_at(&term, config, point, modifiers); + let highlighted_hint = hint::highlighted_at(term, config, point, modifiers); // Update cursor shape. if highlighted_hint.is_some() { @@ -760,7 +760,7 @@ impl Display { let fg = config.ui_config.colors.search_bar_foreground(); let bg = config.ui_config.colors.search_bar_background(); - self.renderer.with_api(&config.ui_config, &size_info, |mut api| { + self.renderer.with_api(&config.ui_config, size_info, |mut api| { api.render_string(glyph_cache, point, fg, bg, &text); }); } @@ -778,7 +778,7 @@ impl Display { let fg = config.ui_config.colors.primary.background; let bg = config.ui_config.colors.normal.red; - self.renderer.with_api(&config.ui_config, &size_info, |mut api| { + self.renderer.with_api(&config.ui_config, size_info, |mut api| { api.render_string(glyph_cache, point, fg, bg, &timing); }); } @@ -801,7 +801,7 @@ impl Display { // Do not render anything if it would obscure the vi mode cursor. if vi_mode_point.map_or(true, |point| point.line != 0 || point.column < column) { let glyph_cache = &mut self.glyph_cache; - self.renderer.with_api(&config.ui_config, &size_info, |mut api| { + self.renderer.with_api(&config.ui_config, size_info, |mut api| { api.render_string(glyph_cache, Point::new(0, column), fg, bg, &text); }); } diff --git a/alacritty/src/display/window.rs b/alacritty/src/display/window.rs index 7302c687..12416700 100644 --- a/alacritty/src/display/window.rs +++ b/alacritty/src/display/window.rs @@ -177,7 +177,7 @@ impl Window { wayland_event_queue: Option<&EventQueue>, ) -> Result<Window> { let window_config = &config.ui_config.window; - let window_builder = Window::get_platform_window(&window_config.title, &window_config); + let window_builder = Window::get_platform_window(&window_config.title, window_config); // Check if we're running Wayland to disable vsync. #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] @@ -186,9 +186,9 @@ impl Window { let is_wayland = false; let windowed_context = - create_gl_window(window_builder.clone(), &event_loop, false, !is_wayland, size) + create_gl_window(window_builder.clone(), event_loop, false, !is_wayland, size) .or_else(|_| { - create_gl_window(window_builder, &event_loop, true, !is_wayland, size) + create_gl_window(window_builder, event_loop, true, !is_wayland, size) })?; // Text cursor. diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs index 64283eb7..cc817f6e 100644 --- a/alacritty/src/event.rs +++ b/alacritty/src/event.rs @@ -65,7 +65,7 @@ const MAX_SEARCH_HISTORY_SIZE: usize = 255; /// Events dispatched through the UI event loop. #[derive(Debug, Clone)] pub enum Event { - TerminalEvent(TerminalEvent), + Terminal(TerminalEvent), DprChanged(f64, (u32, u32)), Scroll(Scroll), ConfigReload(PathBuf), @@ -82,7 +82,7 @@ impl From<Event> for GlutinEvent<'_, Event> { impl From<TerminalEvent> for Event { fn from(event: TerminalEvent) -> Self { - Event::TerminalEvent(event) + Event::Terminal(event) } } @@ -421,11 +421,12 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon self.search_state.origin = self.terminal.vi_mode_cursor.point; self.search_state.display_offset_delta = 0; } else { - let screen_lines = self.terminal.screen_lines(); + let viewport_top = Line(-(self.terminal.grid().display_offset() as i32)) - 1; + let viewport_bottom = viewport_top + self.terminal.bottommost_line(); let last_column = self.terminal.last_column(); self.search_state.origin = match direction { - Direction::Right => Point::new(Line(0), Column(0)), - Direction::Left => Point::new(Line(screen_lines as i32 - 2), last_column), + Direction::Right => Point::new(viewport_top, Column(0)), + Direction::Left => Point::new(viewport_bottom, last_column), }; } @@ -670,6 +671,42 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon } } + /// Expand the selection to the current mouse cursor position. + #[inline] + fn expand_selection(&mut self) { + let selection_type = match self.mouse().click_state { + ClickState::Click => { + if self.modifiers().ctrl() { + SelectionType::Block + } else { + SelectionType::Simple + } + }, + ClickState::DoubleClick => SelectionType::Semantic, + ClickState::TripleClick => SelectionType::Lines, + ClickState::None => return, + }; + + // Load mouse point, treating message bar and padding as the closest cell. + let display_offset = self.terminal().grid().display_offset(); + let point = self.mouse().point(&self.size_info(), display_offset); + + let cell_side = self.mouse().cell_side; + + let selection = match &mut self.terminal_mut().selection { + Some(selection) => selection, + None => return, + }; + + selection.ty = selection_type; + self.update_selection(point, cell_side); + + // Move vi mode cursor to mouse click position. + if self.terminal().mode().contains(TermMode::VI) && !self.search_active() { + self.terminal_mut().vi_mode_cursor.point = point; + } + } + /// Paste a text into the terminal. fn paste(&mut self, text: &str) { if self.search_active() { @@ -743,7 +780,7 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> { self.search_state.dfas = None; } else { // Create search dfas for the new regex string. - self.search_state.dfas = RegexSearch::new(®ex).ok(); + self.search_state.dfas = RegexSearch::new(regex).ok(); // Update search highlighting. self.goto_match(MAX_SEARCH_WHILE_TYPING); @@ -770,7 +807,8 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> { // Reset display offset and cursor position. self.terminal.scroll_display(Scroll::Delta(self.search_state.display_offset_delta)); self.search_state.display_offset_delta = 0; - self.terminal.vi_mode_cursor.point = self.search_state.origin; + self.terminal.vi_mode_cursor.point = + self.search_state.origin.grid_clamp(self.terminal, Boundary::Grid); *self.dirty = true; } @@ -805,7 +843,7 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> { // Store number of lines the viewport had to be moved. let display_offset = self.terminal.grid().display_offset(); - self.search_state.display_offset_delta = old_offset - display_offset as i32; + self.search_state.display_offset_delta += old_offset - display_offset as i32; // Since we found a result, we require no delayed re-search. self.scheduler.unschedule(TimerId::DelayedSearch); @@ -1038,7 +1076,7 @@ impl<N: Notify + OnResize> Processor<N> { match event { // Check for shutdown. - GlutinEvent::UserEvent(Event::TerminalEvent(TerminalEvent::Exit)) => { + GlutinEvent::UserEvent(Event::Terminal(TerminalEvent::Exit)) => { *control_flow = ControlFlow::Exit; return; }, @@ -1181,7 +1219,7 @@ impl<N: Notify + OnResize> Processor<N> { processor.ctx.display.cursor_hidden ^= true; *processor.ctx.dirty = true; }, - Event::TerminalEvent(event) => match event { + Event::Terminal(event) => match event { TerminalEvent::Title(title) => { let ui_config = &processor.ctx.config.ui_config; if ui_config.window.dynamic_title { @@ -1347,7 +1385,7 @@ impl<N: Notify + OnResize> Processor<N> { processor.ctx.display_update_pending.dirty = true; } - let config = match config::reload(&path, &processor.ctx.cli_options) { + let config = match config::reload(path, processor.ctx.cli_options) { Ok(config) => config, Err(_) => return, }; @@ -1398,7 +1436,7 @@ impl<N: Notify + OnResize> Processor<N> { // Disable shadows for transparent windows on macOS. #[cfg(target_os = "macos")] - processor.ctx.window().set_has_shadow(config.ui_config.background_opacity() >= 1.0); + processor.ctx.window().set_has_shadow(config.ui_config.window_opacity() >= 1.0); // Update hint keys. processor.ctx.display.hint_state.update_alphabet(config.ui_config.hints.alphabet()); @@ -1493,6 +1531,6 @@ impl EventProxy { impl EventListener for EventProxy { fn send_event(&self, event: TerminalEvent) { - let _ = self.0.send_event(Event::TerminalEvent(event)); + let _ = self.0.send_event(Event::Terminal(event)); } } diff --git a/alacritty/src/input.rs b/alacritty/src/input.rs index 948ec67f..98a1b723 100644 --- a/alacritty/src/input.rs +++ b/alacritty/src/input.rs @@ -29,7 +29,7 @@ use alacritty_terminal::term::{ClipboardType, SizeInfo, Term, TermMode}; use alacritty_terminal::vi_mode::ViMotion; use crate::clipboard::Clipboard; -use crate::config::{Action, BindingMode, Config, Key, SearchAction, ViAction}; +use crate::config::{Action, BindingMode, Config, Key, MouseAction, SearchAction, ViAction}; use crate::daemon::start_daemon; use crate::display::hint::HintMatch; use crate::display::window::Window; @@ -104,6 +104,7 @@ pub trait ActionContext<T: EventListener> { fn toggle_vi_mode(&mut self) {} fn hint_input(&mut self, _character: char) {} fn trigger_hint(&mut self, _hint: &HintMatch) {} + fn expand_selection(&mut self) {} fn paste(&mut self, _text: &str) {} } @@ -148,19 +149,19 @@ impl<T: EventListener> Execute<T> for Action { ctx.terminal_mut().vi_motion(*motion); ctx.mark_dirty(); }, - Action::ViAction(ViAction::ToggleNormalSelection) => { + Action::Vi(ViAction::ToggleNormalSelection) => { Self::toggle_selection(ctx, SelectionType::Simple); }, - Action::ViAction(ViAction::ToggleLineSelection) => { + Action::Vi(ViAction::ToggleLineSelection) => { Self::toggle_selection(ctx, SelectionType::Lines); }, - Action::ViAction(ViAction::ToggleBlockSelection) => { + Action::Vi(ViAction::ToggleBlockSelection) => { Self::toggle_selection(ctx, SelectionType::Block); }, - Action::ViAction(ViAction::ToggleSemanticSelection) => { + Action::Vi(ViAction::ToggleSemanticSelection) => { Self::toggle_selection(ctx, SelectionType::Semantic); }, - Action::ViAction(ViAction::Open) => { + Action::Vi(ViAction::Open) => { let hint = ctx.display().vi_highlighted_hint.take(); if let Some(hint) = &hint { ctx.mouse_mut().block_hint_launcher = false; @@ -168,7 +169,7 @@ impl<T: EventListener> Execute<T> for Action { } ctx.display().vi_highlighted_hint = hint; }, - Action::ViAction(ViAction::SearchNext) => { + Action::Vi(ViAction::SearchNext) => { let terminal = ctx.terminal(); let direction = ctx.search_direction(); let vi_point = terminal.vi_mode_cursor.point; @@ -182,7 +183,7 @@ impl<T: EventListener> Execute<T> for Action { ctx.mark_dirty(); } }, - Action::ViAction(ViAction::SearchPrevious) => { + Action::Vi(ViAction::SearchPrevious) => { let terminal = ctx.terminal(); let direction = ctx.search_direction().opposite(); let vi_point = terminal.vi_mode_cursor.point; @@ -196,7 +197,7 @@ impl<T: EventListener> Execute<T> for Action { ctx.mark_dirty(); } }, - Action::ViAction(ViAction::SearchStart) => { + Action::Vi(ViAction::SearchStart) => { let terminal = ctx.terminal(); let origin = terminal.vi_mode_cursor.point.sub(terminal, Boundary::None, 1); @@ -205,7 +206,7 @@ impl<T: EventListener> Execute<T> for Action { ctx.mark_dirty(); } }, - Action::ViAction(ViAction::SearchEnd) => { + Action::Vi(ViAction::SearchEnd) => { let terminal = ctx.terminal(); let origin = terminal.vi_mode_cursor.point.add(terminal, Boundary::None, 1); @@ -214,25 +215,24 @@ impl<T: EventListener> Execute<T> for Action { ctx.mark_dirty(); } }, - Action::SearchAction(SearchAction::SearchFocusNext) => { + Action::Search(SearchAction::SearchFocusNext) => { ctx.advance_search_origin(ctx.search_direction()); }, - Action::SearchAction(SearchAction::SearchFocusPrevious) => { + Action::Search(SearchAction::SearchFocusPrevious) => { let direction = ctx.search_direction().opposite(); ctx.advance_search_origin(direction); }, - Action::SearchAction(SearchAction::SearchConfirm) => ctx.confirm_search(), - Action::SearchAction(SearchAction::SearchCancel) => ctx.cancel_search(), - Action::SearchAction(SearchAction::SearchClear) => { + Action::Search(SearchAction::SearchConfirm) => ctx.confirm_search(), + Action::Search(SearchAction::SearchCancel) => ctx.cancel_search(), + Action::Search(SearchAction::SearchClear) => { let direction = ctx.search_direction(); ctx.cancel_search(); ctx.start_search(direction); }, - Action::SearchAction(SearchAction::SearchDeleteWord) => ctx.search_pop_word(), - Action::SearchAction(SearchAction::SearchHistoryPrevious) => { - ctx.search_history_previous() - }, - Action::SearchAction(SearchAction::SearchHistoryNext) => ctx.search_history_next(), + Action::Search(SearchAction::SearchDeleteWord) => ctx.search_pop_word(), + Action::Search(SearchAction::SearchHistoryPrevious) => ctx.search_history_previous(), + Action::Search(SearchAction::SearchHistoryNext) => ctx.search_history_next(), + Action::Mouse(MouseAction::ExpandSelection) => ctx.expand_selection(), Action::SearchForward => ctx.start_search(Direction::Right), Action::SearchBackward => ctx.start_search(Direction::Left), Action::Copy => ctx.copy_selection(ClipboardType::Clipboard), @@ -533,51 +533,12 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { let display_offset = self.ctx.terminal().grid().display_offset(); let point = self.ctx.mouse().point(&self.ctx.size_info(), display_offset); - match button { - MouseButton::Left => self.on_left_click(point), - MouseButton::Right => self.on_right_click(point), - // Do nothing when using buttons other than LMB. - _ => self.ctx.mouse_mut().click_state = ClickState::None, + if let MouseButton::Left = button { + self.on_left_click(point) } } } - /// Handle selection expansion on right click. - fn on_right_click(&mut self, point: Point) { - match self.ctx.mouse().click_state { - ClickState::Click => { - let selection_type = if self.ctx.modifiers().ctrl() { - SelectionType::Block - } else { - SelectionType::Simple - }; - - self.expand_selection(point, selection_type); - }, - ClickState::DoubleClick => self.expand_selection(point, SelectionType::Semantic), - ClickState::TripleClick => self.expand_selection(point, SelectionType::Lines), - ClickState::None => (), - } - } - - /// Expand existing selection. - fn expand_selection(&mut self, point: Point, selection_type: SelectionType) { - let cell_side = self.ctx.mouse().cell_side; - - let selection = match &mut self.ctx.terminal_mut().selection { - Some(selection) => selection, - None => return, - }; - - selection.ty = selection_type; - self.ctx.update_selection(point, cell_side); - - // Move vi mode cursor to mouse click position. - if self.ctx.terminal().mode().contains(TermMode::VI) && !self.ctx.search_active() { - self.ctx.terminal_mut().vi_mode_cursor.point = point; - } - } - /// Handle left click selection and vi mode cursor movement. fn on_left_click(&mut self, point: Point) { let side = self.ctx.mouse().cell_side; @@ -751,8 +712,9 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { } else { match state { ElementState::Pressed => { - self.process_mouse_bindings(button); + // Process mouse press before bindings to update the `click_state`. self.on_mouse_press(button); + self.process_mouse_bindings(button); }, ElementState::Released => self.on_mouse_release(button), } @@ -1036,7 +998,7 @@ mod tests { } fn terminal(&self) -> &Term<T> { - &self.terminal + self.terminal } fn terminal_mut(&mut self) -> &mut Term<T> { @@ -1140,6 +1102,7 @@ mod tests { let mut mouse = Mouse { click_state: $initial_state, + last_click_button: $initial_button, ..Mouse::default() }; @@ -1241,7 +1204,7 @@ mod tests { }, window_id: unsafe { std::mem::transmute_copy(&0) }, }, - end_state: ClickState::None, + end_state: ClickState::Click, } test_clickstate! { diff --git a/alacritty/src/logging.rs b/alacritty/src/logging.rs index 36d20fa3..7cef3887 100644 --- a/alacritty/src/logging.rs +++ b/alacritty/src/logging.rs @@ -13,7 +13,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use glutin::event_loop::EventLoopProxy; -use log::{self, Level}; +use log::{self, Level, LevelFilter}; use crate::cli::Options; use crate::event::Event; @@ -29,7 +29,7 @@ pub fn initialize( options: &Options, event_proxy: EventLoopProxy<Event>, ) -> Result<Option<PathBuf>, log::SetLoggerError> { - log::set_max_level(options.log_level); + log::set_max_level(options.log_level()); let logger = Logger::new(event_proxy); let path = logger.file_path(); @@ -102,13 +102,13 @@ impl log::Log for Logger { let index = record.target().find(':').unwrap_or_else(|| record.target().len()); let target = &record.target()[..index]; - // Only log our own crates. - if !self.enabled(record.metadata()) || !ALLOWED_TARGETS.contains(&target) { + // Only log our own crates, except when logging at Level::Trace. + if !self.enabled(record.metadata()) || !is_allowed_target(record.level(), target) { return; } // Create log message for the given `record` and `target`. - let message = create_log_message(record, &target); + let message = create_log_message(record, target); if let Ok(mut logfile) = self.logfile.lock() { // Write to logfile. @@ -149,6 +149,14 @@ fn create_log_message(record: &log::Record<'_>, target: &str) -> String { message } +/// Check if log messages from a crate should be logged. +fn is_allowed_target(level: Level, target: &str) -> bool { + match (level, log::max_level()) { + (Level::Error, LevelFilter::Trace) | (Level::Warn, LevelFilter::Trace) => true, + _ => ALLOWED_TARGETS.contains(&target), + } +} + struct OnDemandLogFile { file: Option<LineWriter<File>>, created: Arc<AtomicBool>, diff --git a/alacritty/src/main.rs b/alacritty/src/main.rs index 85a4fc5e..f6e3c1d0 100644 --- a/alacritty/src/main.rs +++ b/alacritty/src/main.rs @@ -1,7 +1,7 @@ //! Alacritty - The GPU Enhanced Terminal. #![warn(rust_2018_idioms, future_incompatible)] -#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)] +#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use)] #![cfg_attr(feature = "cargo-clippy", deny(warnings))] // With the default subsystem, 'console', windows creates an additional console // window for the program. diff --git a/alacritty/src/renderer/mod.rs b/alacritty/src/renderer/mod.rs index 8d0c4b68..71ed6e28 100644 --- a/alacritty/src/renderer/mod.rs +++ b/alacritty/src/renderer/mod.rs @@ -194,7 +194,7 @@ impl GlyphCache { let size = font.size(); // Load regular font. - let regular_desc = Self::make_desc(&font.normal(), Slant::Normal, Weight::Normal); + let regular_desc = Self::make_desc(font.normal(), Slant::Normal, Weight::Normal); let regular = Self::load_regular_font(rasterizer, ®ular_desc, size)?; @@ -236,7 +236,7 @@ impl GlyphCache { error!("{}", err); let fallback_desc = - Self::make_desc(&Font::default().normal(), Slant::Normal, Weight::Normal); + Self::make_desc(Font::default().normal(), Slant::Normal, Weight::Normal); rasterizer.load_font(&fallback_desc, size) }, } @@ -375,7 +375,7 @@ impl GlyphCache { /// Calculate font metrics without access to a glyph cache. pub fn static_metrics(font: Font, dpr: f64) -> Result<crossfont::Metrics, crossfont::Error> { let mut rasterizer = crossfont::Rasterizer::new(dpr as f32, font.use_thin_strokes)?; - let regular_desc = GlyphCache::make_desc(&font.normal(), Slant::Normal, Weight::Normal); + let regular_desc = GlyphCache::make_desc(font.normal(), Slant::Normal, Weight::Normal); let regular = Self::load_regular_font(&mut rasterizer, ®ular_desc, font.size())?; rasterizer.get_glyph(GlyphKey { font_key: regular, character: 'm', size: font.size() })?; @@ -785,7 +785,7 @@ impl Drop for QuadRenderer { impl<'a> RenderApi<'a> { pub fn clear(&self, color: Rgb) { unsafe { - let alpha = self.config.background_opacity(); + let alpha = self.config.window_opacity(); gl::ClearColor( (f32::from(color.r) / 255.0).min(1.0) * alpha, (f32::from(color.g) / 255.0).min(1.0) * alpha, @@ -1325,12 +1325,12 @@ impl Atlas { } // If there's not enough room in current row, go onto next one. - if !self.room_in_row(&glyph) { + if !self.room_in_row(glyph) { self.advance_row()?; } // If there's still not room, there's nothing that can be done here.. - if !self.room_in_row(&glyph) { + if !self.room_in_row(glyph) { return Err(AtlasInsertError::Full); } diff --git a/alacritty/src/renderer/rects.rs b/alacritty/src/renderer/rects.rs index 591c9f46..77c22011 100644 --- a/alacritty/src/renderer/rects.rs +++ b/alacritty/src/renderer/rects.rs @@ -158,9 +158,9 @@ impl RenderLines { /// Update the stored lines with the next cell info. #[inline] pub fn update(&mut self, cell: &RenderableCell) { - self.update_flag(&cell, Flags::UNDERLINE); - self.update_flag(&cell, Flags::DOUBLE_UNDERLINE); - self.update_flag(&cell, Flags::STRIKEOUT); + self.update_flag(cell, Flags::UNDERLINE); + self.update_flag(cell, Flags::DOUBLE_UNDERLINE); + self.update_flag(cell, Flags::STRIKEOUT); } /// Update the lines for a specific flag. diff --git a/extra/windows/alacritty.ico b/alacritty/windows/alacritty.ico Binary files differindex a84db555..a84db555 100644 --- a/extra/windows/alacritty.ico +++ b/alacritty/windows/alacritty.ico diff --git a/extra/windows/alacritty.manifest b/alacritty/windows/alacritty.manifest index 82039bf7..82039bf7 100644 --- a/extra/windows/alacritty.manifest +++ b/alacritty/windows/alacritty.manifest diff --git a/extra/windows/windows.rc b/alacritty/windows/windows.rc index b265d4bc..b265d4bc 100644 --- a/extra/windows/windows.rc +++ b/alacritty/windows/windows.rc diff --git a/extra/windows/wix/alacritty.wxs b/alacritty/windows/wix/alacritty.wxs index 961faeb8..a8c97d39 100644 --- a/extra/windows/wix/alacritty.wxs +++ b/alacritty/windows/wix/alacritty.wxs @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="windows-1252"?> <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"> - <Product Name="Alacritty" Id="*" UpgradeCode="87c21c74-dbd5-4584-89d5-46d9cd0c40a7" Language="1033" Codepage="1252" Version="0.8.0-dev" Manufacturer="Alacritty"> + <Product Name="Alacritty" Id="*" UpgradeCode="87c21c74-dbd5-4584-89d5-46d9cd0c40a7" Language="1033" Codepage="1252" Version="0.10.0-dev" Manufacturer="Alacritty"> <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine"/> <MajorUpgrade AllowSameVersionUpgrades="yes" DowngradeErrorMessage="A newer version of [ProductName] is already installed."/> <Icon Id="AlacrittyIco" SourceFile=".\extra\windows\alacritty.ico"/> diff --git a/extra/windows/wix/license.rtf b/alacritty/windows/wix/license.rtf index ea025032..ea025032 100644 --- a/extra/windows/wix/license.rtf +++ b/alacritty/windows/wix/license.rtf diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index 89bc26a5..c25c4cab 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "alacritty_terminal" -version = "0.13.1-dev" +version = "0.15.1-dev" authors = ["Christian Duerr <contact@christianduerr.com>", "Joe Wilm <joe@jwilm.com>"] license = "Apache-2.0" description = "Library for writing terminal emulators" @@ -28,16 +28,16 @@ regex-automata = "0.1.9" dirs = "3.0.1" [target.'cfg(unix)'.dependencies] -nix = "0.20.0" +nix = "0.22.0" signal-hook = { version = "0.1", features = ["mio-support"] } [target.'cfg(windows)'.dependencies] miow = "0.3" winapi = { version = "0.3.7", features = [ "impl-default", "basetsd", "libloaderapi", "minwindef", "ntdef", "processthreadsapi", "winbase", - "wincon", "wincontypes", "winerror", "winnt", "winuser", + "wincon", "wincontypes", "winerror", "winnt", "winuser", "consoleapi", ]} -mio-anonymous-pipes = "0.1" +mio-anonymous-pipes = "0.2" [dev-dependencies] serde_json = "1.0.0" diff --git a/alacritty_terminal/src/event_loop.rs b/alacritty_terminal/src/event_loop.rs index 098ee896..b4c0a5e4 100644 --- a/alacritty_terminal/src/event_loop.rs +++ b/alacritty_terminal/src/event_loop.rs @@ -22,8 +22,11 @@ use crate::term::{SizeInfo, Term}; use crate::thread; use crate::tty; -/// Max bytes to read from the PTY. -const MAX_READ: usize = u16::max_value() as usize; +/// Max bytes to read from the PTY before forced terminal synchronization. +const READ_BUFFER_SIZE: usize = 0x10_0000; + +/// Max bytes to read from the PTY while the terminal is locked. +const MAX_LOCKED_READ: usize = u16::max_value() as usize; /// Messages that may be sent to the `EventLoop`. #[derive(Debug)] @@ -213,48 +216,58 @@ where where X: Write, { + let mut unprocessed = 0; let mut processed = 0; + + // Reserve the next terminal lock for PTY reading. + let _terminal_lease = Some(self.terminal.lease()); let mut terminal = None; loop { - match self.pty.reader().read(buf) { - Ok(0) => break, - Ok(got) => { - // Record bytes read; used to limit time spent in pty_read. - processed += got; - - // Send a copy of bytes read to a subscriber. Used for - // example with ref test recording. - writer = writer.map(|w| { - w.write_all(&buf[..got]).unwrap(); - w - }); - - // 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`. - if terminal.is_none() { - terminal = Some(self.terminal.lock()); - } - let terminal = terminal.as_mut().unwrap(); - - // Run the parser. - for byte in &buf[..got] { - state.parser.advance(&mut **terminal, *byte); - } - - // Exit if we've processed enough bytes. - if processed > MAX_READ { - break; - } - }, + // Read from the PTY. + match self.pty.reader().read(&mut buf[unprocessed..]) { + // This is received on Windows/macOS when no more data is readable from the PTY. + Ok(0) if unprocessed == 0 => break, + Ok(got) => unprocessed += got, Err(err) => match err.kind() { ErrorKind::Interrupted | ErrorKind::WouldBlock => { - break; + // Go back to mio if we're caught up on parsing and the PTY would block. + if unprocessed == 0 { + break; + } }, _ => return Err(err), }, } + + // Attempt to lock the terminal. + let terminal = match &mut terminal { + Some(terminal) => terminal, + None => terminal.insert(match self.terminal.try_lock_unfair() { + // Force block if we are at the buffer size limit. + None if unprocessed >= READ_BUFFER_SIZE => self.terminal.lock_unfair(), + None => continue, + Some(terminal) => terminal, + }), + }; + + // Write a copy of the bytes to the ref test file. + if let Some(writer) = &mut writer { + writer.write_all(&buf[..unprocessed]).unwrap(); + } + + // Parse the incoming bytes. + for byte in &buf[..unprocessed] { + state.parser.advance(&mut **terminal, *byte); + } + + processed += unprocessed; + unprocessed = 0; + + // Assure we're not blocking the terminal too long unnecessarily. + if processed >= MAX_LOCKED_READ { + break; + } } // Queue terminal redraw unless all processed bytes were synchronized. @@ -300,7 +313,7 @@ where pub fn spawn(mut self) -> JoinHandle<(Self, State)> { thread::spawn_named("PTY reader", move || { let mut state = State::default(); - let mut buf = [0u8; MAX_READ]; + let mut buf = [0u8; READ_BUFFER_SIZE]; let mut tokens = (0..).map(Into::into); @@ -381,7 +394,7 @@ where // This sucks, but checking the process is either racy or // blocking. #[cfg(target_os = "linux")] - if err.kind() == ErrorKind::Other { + if err.raw_os_error() == Some(libc::EIO) { continue; } @@ -418,3 +431,32 @@ where }) } } + +trait OptionInsert { + type T; + fn insert(&mut self, value: Self::T) -> &mut Self::T; +} + +// TODO: Remove when MSRV is >= 1.53.0. +// +/// Trait implementation to support Rust version < 1.53.0. +/// +/// This is taken [from STD], further license information can be found in the [rust-lang/rust +/// repository]. +/// +/// +/// [from STD]: https://github.com/rust-lang/rust/blob/6e0b554619a3bb7e75b3334e97f191af20ef5d76/library/core/src/option.rs#L829-L858 +/// [rust-lang/rust repository]: https://github.com/rust-lang/rust/blob/master/LICENSE-MIT +impl<T> OptionInsert for Option<T> { + type T = T; + + fn insert(&mut self, value: T) -> &mut T { + *self = Some(value); + + match self { + Some(v) => v, + // SAFETY: the code above just filled the option + None => unsafe { std::hint::unreachable_unchecked() }, + } + } +} diff --git a/alacritty_terminal/src/lib.rs b/alacritty_terminal/src/lib.rs index 59b2fe05..ea4a2a24 100644 --- a/alacritty_terminal/src/lib.rs +++ b/alacritty_terminal/src/lib.rs @@ -1,7 +1,7 @@ //! Alacritty - The GPU Enhanced Terminal. #![warn(rust_2018_idioms, future_incompatible)] -#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)] +#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use)] #![cfg_attr(feature = "cargo-clippy", deny(warnings))] pub mod ansi; diff --git a/alacritty_terminal/src/sync.rs b/alacritty_terminal/src/sync.rs index 43148a78..848bab62 100644 --- a/alacritty_terminal/src/sync.rs +++ b/alacritty_terminal/src/sync.rs @@ -21,6 +21,14 @@ impl<T> FairMutex<T> { FairMutex { data: Mutex::new(data), next: Mutex::new(()) } } + /// Acquire a lease to reserve the mutex lock. + /// + /// This will prevent others from acquiring a terminal lock, but block if anyone else is + /// already holding a lease. + pub fn lease(&self) -> MutexGuard<'_, ()> { + self.next.lock() + } + /// Lock the mutex. pub fn lock(&self) -> MutexGuard<'_, T> { // Must bind to a temporary or the lock will be freed before going @@ -28,4 +36,14 @@ impl<T> FairMutex<T> { let _next = self.next.lock(); self.data.lock() } + + /// Unfairly lock the mutex. + pub fn lock_unfair(&self) -> MutexGuard<'_, T> { + self.data.lock() + } + + /// Unfairly try to lock the mutex. + pub fn try_lock_unfair(&self) -> Option<MutexGuard<'_, T>> { + self.data.try_lock() + } } diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 959f99ef..90af5ce7 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -419,7 +419,7 @@ impl<T> Term<T> { // Skip over cells until next tab-stop once a tab was found. if tab_mode { - if self.tabs[column] { + if self.tabs[column] || cell.c != ' ' { tab_mode = false; } else { continue; @@ -531,8 +531,10 @@ impl<T> Term<T> { // Clamp vi cursor to viewport. let vi_point = self.vi_mode_cursor.point; - self.vi_mode_cursor.point.column = min(vi_point.column, Column(num_cols - 1)); - self.vi_mode_cursor.point.line = min(vi_point.line, Line(num_lines as i32 - 1)); + let viewport_top = Line(-(self.grid.display_offset() as i32)); + let viewport_bottom = viewport_top + self.bottommost_line(); + self.vi_mode_cursor.point.line = max(min(vi_point.line, viewport_bottom), viewport_top); + self.vi_mode_cursor.point.column = min(vi_point.column, self.last_column()); // Reset scrolling region. self.scroll_region = Line(0)..Line(self.screen_lines() as i32); @@ -579,6 +581,12 @@ impl<T> Term<T> { self.selection = self.selection.take().and_then(|s| s.rotate(self, ®ion, -(lines as i32))); + // Scroll vi mode cursor. + let line = &mut self.vi_mode_cursor.point.line; + if region.start <= *line && region.end > *line { + *line = min(*line + lines, region.end - 1); + } + // Scroll between origin and bottom self.grid.scroll_down(®ion, lines); } @@ -598,6 +606,14 @@ impl<T> Term<T> { // Scroll selection. self.selection = self.selection.take().and_then(|s| s.rotate(self, ®ion, lines as i32)); + // Scroll vi mode cursor. + let viewport_top = Line(-(self.grid.display_offset() as i32)); + let top = if region.start == 0 { viewport_top } else { region.start }; + let line = &mut self.vi_mode_cursor.point.line; + if (top <= *line) && region.end > *line { + *line = max(*line - lines, top); + } + // Scroll from origin to bottom less number of lines. self.grid.scroll_up(®ion, lines); } @@ -795,9 +811,9 @@ impl<T> Term<T> { // Remove wide char and spacer. let wide = cursor_cell.flags.contains(Flags::WIDE_CHAR); let point = self.grid.cursor.point; - if wide && point.column + 1 < self.columns() { + if wide && point.column < self.last_column() { self.grid[point.line][point.column + 1].flags.remove(Flags::WIDE_CHAR_SPACER); - } else { + } else if point.column > 0 { self.grid[point.line][point.column - 1].clear_wide(); } @@ -2069,6 +2085,62 @@ mod tests { use crate::term::cell::{Cell, Flags}; #[test] + fn scroll_display_page_up() { + let size = SizeInfo::new(5., 10., 1.0, 1.0, 0.0, 0.0, false); + let mut term = Term::new(&MockConfig::default(), size, ()); + + // Create 11 lines of scrollback. + for _ in 0..20 { + term.newline(); + } + + // Scrollable amount to top is 11. + term.scroll_display(Scroll::PageUp); + assert_eq!(term.vi_mode_cursor.point, Point::new(Line(-1), Column(0))); + assert_eq!(term.grid.display_offset(), 10); + + // Scrollable amount to top is 1. + term.scroll_display(Scroll::PageUp); + assert_eq!(term.vi_mode_cursor.point, Point::new(Line(-2), Column(0))); + assert_eq!(term.grid.display_offset(), 11); + + // Scrollable amount to top is 0. + term.scroll_display(Scroll::PageUp); + assert_eq!(term.vi_mode_cursor.point, Point::new(Line(-2), Column(0))); + assert_eq!(term.grid.display_offset(), 11); + } + + #[test] + fn scroll_display_page_down() { + let size = SizeInfo::new(5., 10., 1.0, 1.0, 0.0, 0.0, false); + let mut term = Term::new(&MockConfig::default(), size, ()); + + // Create 11 lines of scrollback. + for _ in 0..20 { + term.newline(); + } + + // Change display_offset to topmost. + term.grid_mut().scroll_display(Scroll::Top); + term.vi_mode_cursor = ViModeCursor::new(Point::new(Line(-11), Column(0))); + + // Scrollable amount to bottom is 11. + term.scroll_display(Scroll::PageDown); + assert_eq!(term.vi_mode_cursor.point, Point::new(Line(-1), Column(0))); + assert_eq!(term.grid.display_offset(), 1); + + // Scrollable amount to bottom is 1. + term.scroll_display(Scroll::PageDown); + assert_eq!(term.vi_mode_cursor.point, Point::new(Line(0), Column(0))); + assert_eq!(term.grid.display_offset(), 0); + + // Scrollable amount to bottom is 0. + term.scroll_display(Scroll::PageDown); + assert_eq!(term.vi_mode_cursor.point, Point::new(Line(0), Column(0))); + assert_eq!(term.grid.display_offset(), 0); + } + + #[test] fn semantic_selection_works() { let size = SizeInfo::new(5., 3., 1.0, 1.0, 0.0, 0.0, false); let mut term = Term::new(&MockConfig::default(), size, ()); diff --git a/alacritty_terminal/src/term/search.rs b/alacritty_terminal/src/term/search.rs index bf7f43d1..ff6060af 100644 --- a/alacritty_terminal/src/term/search.rs +++ b/alacritty_terminal/src/term/search.rs @@ -88,14 +88,14 @@ impl<T> Term<T> { _ => end.sub(self, Boundary::None, 1), }; - let mut regex_iter = RegexIter::new(start, end, Direction::Right, &self, dfas).peekable(); + let mut regex_iter = RegexIter::new(start, end, Direction::Right, self, dfas).peekable(); // Check if there's any match at all. let first_match = regex_iter.peek()?.clone(); let regex_match = regex_iter .find(|regex_match| { - let match_point = Self::match_side(®ex_match, side); + let match_point = Self::match_side(regex_match, side); // If the match's point is beyond the origin, we're done. match_point.line < start.line @@ -127,14 +127,14 @@ impl<T> Term<T> { _ => end.add(self, Boundary::None, 1), }; - let mut regex_iter = RegexIter::new(start, end, Direction::Left, &self, dfas).peekable(); + let mut regex_iter = RegexIter::new(start, end, Direction::Left, self, dfas).peekable(); // Check if there's any match at all. let first_match = regex_iter.peek()?.clone(); let regex_match = regex_iter .find(|regex_match| { - let match_point = Self::match_side(®ex_match, side); + let match_point = Self::match_side(regex_match, side); // If the match's point is beyond the origin, we're done. match_point.line > start.line diff --git a/alacritty_terminal/src/tty/unix.rs b/alacritty_terminal/src/tty/unix.rs index ba59bb66..a3c35f95 100644 --- a/alacritty_terminal/src/tty/unix.rs +++ b/alacritty_terminal/src/tty/unix.rs @@ -82,10 +82,6 @@ fn set_controlling_terminal(fd: c_int) { #[derive(Debug)] struct Passwd<'a> { name: &'a str, - passwd: &'a str, - uid: libc::uid_t, - gid: libc::gid_t, - gecos: &'a str, dir: &'a str, shell: &'a str, } @@ -122,10 +118,6 @@ fn get_pw_entry(buf: &mut [i8; 1024]) -> Passwd<'_> { // Build a borrowed Passwd struct. Passwd { name: unsafe { CStr::from_ptr(entry.pw_name).to_str().unwrap() }, - passwd: unsafe { CStr::from_ptr(entry.pw_passwd).to_str().unwrap() }, - uid: entry.pw_uid, - gid: entry.pw_gid, - gecos: unsafe { CStr::from_ptr(entry.pw_gecos).to_str().unwrap() }, dir: unsafe { CStr::from_ptr(entry.pw_dir).to_str().unwrap() }, shell: unsafe { CStr::from_ptr(entry.pw_shell).to_str().unwrap() }, } diff --git a/alacritty_terminal/src/vi_mode.rs b/alacritty_terminal/src/vi_mode.rs index 6f370642..5bf5eaed 100644 --- a/alacritty_terminal/src/vi_mode.rs +++ b/alacritty_terminal/src/vi_mode.rs @@ -1,4 +1,4 @@ -use std::cmp::{max, min}; +use std::cmp::min; use alacritty_config_derive::ConfigDeserialize; @@ -160,21 +160,11 @@ impl ViModeCursor { /// Get target cursor point for vim-like page movement. #[must_use = "this returns the result of the operation, without modifying the original"] pub fn scroll<T: EventListener>(mut self, term: &Term<T>, lines: i32) -> Self { - // Check number of lines the cursor needs to be moved. - let overscroll = if lines > 0 { - let max_scroll = term.history_size() - term.grid().display_offset(); - max(0, lines - max_scroll as i32) - } else { - let max_scroll = term.grid().display_offset(); - min(0, lines + max_scroll as i32) - }; - // Clamp movement to within visible region. - let line = (self.point.line - overscroll).grid_clamp(term, Boundary::Grid); + let line = (self.point.line - lines).grid_clamp(term, Boundary::Grid); // Find the first occupied cell after scrolling has been performed. - let target_line = (self.point.line - lines).grid_clamp(term, Boundary::Grid); - let column = first_occupied_in_line(term, target_line).unwrap_or_default().column; + let column = first_occupied_in_line(term, line).unwrap_or_default().column; // Move cursor. self.point = Point::new(line, column); @@ -388,6 +378,7 @@ fn is_boundary<T>(term: &Term<T>, point: Point, direction: Direction) -> bool { mod tests { use super::*; + use crate::ansi::Handler; use crate::config::MockConfig; use crate::index::{Column, Line}; use crate::term::{SizeInfo, Term}; @@ -756,4 +747,73 @@ mod tests { cursor = cursor.motion(&mut term, ViMotion::WordLeft); assert_eq!(cursor.point, Point::new(Line(0), Column(0))); } + + #[test] + fn scroll_simple() { + let mut term = term(); + + // Create 1 line of scrollback. + for _ in 0..20 { + term.newline(); + } + + let mut cursor = ViModeCursor::new(Point::new(Line(0), Column(0))); + + cursor = cursor.scroll(&term, -1); + assert_eq!(cursor.point, Point::new(Line(1), Column(0))); + + cursor = cursor.scroll(&term, 1); + assert_eq!(cursor.point, Point::new(Line(0), Column(0))); + + cursor = cursor.scroll(&term, 1); + assert_eq!(cursor.point, Point::new(Line(-1), Column(0))); + } + + #[test] + fn scroll_over_top() { + let mut term = term(); + + // Create 40 lines of scrollback. + for _ in 0..59 { + term.newline(); + } + + let mut cursor = ViModeCursor::new(Point::new(Line(19), Column(0))); + + cursor = cursor.scroll(&term, 20); + assert_eq!(cursor.point, Point::new(Line(-1), Column(0))); + + cursor = cursor.scroll(&term, 20); + assert_eq!(cursor.point, Point::new(Line(-21), Column(0))); + + cursor = cursor.scroll(&term, 20); + assert_eq!(cursor.point, Point::new(Line(-40), Column(0))); + + cursor = cursor.scroll(&term, 20); + assert_eq!(cursor.point, Point::new(Line(-40), Column(0))); + } + + #[test] + fn scroll_over_bottom() { + let mut term = term(); + + // Create 40 lines of scrollback. + for _ in 0..59 { + term.newline(); + } + + let mut cursor = ViModeCursor::new(Point::new(Line(-40), Column(0))); + + cursor = cursor.scroll(&term, -20); + assert_eq!(cursor.point, Point::new(Line(-20), Column(0))); + + cursor = cursor.scroll(&term, -20); + assert_eq!(cursor.point, Point::new(Line(0), Column(0))); + + cursor = cursor.scroll(&term, -20); + assert_eq!(cursor.point, Point::new(Line(19), Column(0))); + + cursor = cursor.scroll(&term, -20); + assert_eq!(cursor.point, Point::new(Line(19), Column(0))); + } } diff --git a/extra/alacritty.info b/extra/alacritty.info index eaa73f07..29171438 100644 --- a/extra/alacritty.info +++ b/extra/alacritty.info @@ -27,7 +27,7 @@ alacritty+common|base fragment for alacritty, OTbs, am, bce, km, mir, msgr, xenl, AX, XT, colors#8, cols#80, it#8, lines#24, pairs#64, acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, - bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, civis=\E[?25l, + bel=^G, bold=\E[1m, cbt=\E[Z, civis=\E[?25l, clear=\E[H\E[2J, cnorm=\E[?12l\E[?25h, cr=\r, csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H, cud=\E[%p1%dB, cud1=\n, cuf=\E[%p1%dC, cuf1=\E[C, diff --git a/extra/alacritty.man b/extra/alacritty.man index 48b05de9..fbc28c25 100644 --- a/extra/alacritty.man +++ b/extra/alacritty.man @@ -1,4 +1,4 @@ -.TH ALACRITTY "1" "August 2018" "alacritty 0.8.0-dev" "User Commands" +.TH ALACRITTY "1" "August 2018" "alacritty 0.10.0-dev" "User Commands" .SH NAME Alacritty \- A fast, cross-platform, OpenGL terminal emulator .SH "SYNOPSIS" diff --git a/extra/linux/io.alacritty.Alacritty.appdata.xml b/extra/linux/io.alacritty.Alacritty.appdata.xml index d9f5e1f2..af82c474 100644 --- a/extra/linux/io.alacritty.Alacritty.appdata.xml +++ b/extra/linux/io.alacritty.Alacritty.appdata.xml @@ -28,9 +28,6 @@ </keywords> <url type="homepage">https://github.com/alacritty/alacritty</url> <url type="bugtracker">https://github.com/alacritty/alacritty/issues</url> - <releases> - <release version="0.7.0-dev" date="2019-06-16" unix_timestamp="1560694196"/> - </releases> <update_contact>https://github.com/alacritty/alacritty/blob/master/CONTRIBUTING.md#contact</update_contact> <developer_name>Christian Duerr</developer_name> </component> diff --git a/extra/osx/Alacritty.app/Contents/Info.plist b/extra/osx/Alacritty.app/Contents/Info.plist index 8c88e53b..4a7460f1 100644 --- a/extra/osx/Alacritty.app/Contents/Info.plist +++ b/extra/osx/Alacritty.app/Contents/Info.plist @@ -15,7 +15,7 @@ <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> - <string>0.8.0-dev</string> + <string>0.10.0-dev</string> <key>CFBundleSupportedPlatforms</key> <array> <string>MacOSX</string> diff --git a/extra/windows b/extra/windows new file mode 120000 index 00000000..1c83ac5e --- /dev/null +++ b/extra/windows @@ -0,0 +1 @@ +../alacritty/windows
\ No newline at end of file |