diff options
| author | Ayose <ayosec@gmail.com> | 2022-10-16 19:31:54 +0100 |
|---|---|---|
| committer | Ayose <ayosec@gmail.com> | 2022-10-16 19:31:54 +0100 |
| commit | 2eb9812849bc849cb634e2d9ada3dfa231603d43 (patch) | |
| tree | 29872ba415a25e3f433dd809789773aa254b9d04 | |
| parent | 83aceede5b0de5152c5e448d79289d7237f1ee9e (diff) | |
| parent | 269b6e3dba66f54ff6d83a1d26142683dd191905 (diff) | |
| download | r-alacritty-2eb9812849bc849cb634e2d9ada3dfa231603d43.tar.gz r-alacritty-2eb9812849bc849cb634e2d9ada3dfa231603d43.tar.bz2 r-alacritty-2eb9812849bc849cb634e2d9ada3dfa231603d43.zip | |
Merge remote-tracking branch 'vendor/master' into graphics
66 files changed, 1596 insertions, 657 deletions
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6bf947b7..1d16087b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,7 +50,7 @@ jobs: - name: Crate msi installer run: | ./WiX.*/tools/candle.exe -nologo -arch "x64" -ext WixUIExtension -ext WixUtilExtension \ - -out "./alacritty.wixobj" "extra/windows/wix/alacritty.wxs" + -out "./alacritty.wixobj" "alacritty/windows/wix/alacritty.wxs" ./WiX.*/tools/light.exe -nologo -ext WixUIExtension -ext WixUtilExtension \ -out "./Alacritty-${GITHUB_REF##*/}-installer.msi" -sice:ICE61 -sice:ICE91 \ "./alacritty.wixobj" diff --git a/CHANGELOG.md b/CHANGELOG.md index f93e326d..129822ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,9 @@ 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.11.0-dev +## 0.12.0-dev + +## 0.11.0 ### Packaging @@ -27,9 +29,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Config option `cursor.blink_timeout` to timeout cursor blinking after inactivity - Escape sequence to set hyperlinks (`OSC 8 ; params ; URI ST`) - Config `hints.enabled.hyperlinks` for hyperlink escape sequence hint highlight +- `window.decorations_theme_variant` to control both Wayland CSD and GTK theme variant on X11 +- Support for inline input method ### Changed +- No longer renders to macos and x11 windows that are fully occluded / not directly visible - The `--help` output was reworked with a new colorful syntax - OSC 52 is now disabled on unfocused windows - `SpawnNewInstance` no longer inherits initial `--command` @@ -37,6 +42,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Deprecated `colors.search.bar`, use `colors.footer_bar` instead - On macOS, Alacritty now reads `AppleFontSmoothing` from user defaults to control font smoothing - Warn when either `columns` or `lines` is non-zero, but not both +- Client side decorations should have proper text rendering now on Wayland +- Config option `window.gtk_theme_variant`, you should use `window.decorations_theme_variant` instead +- `--class` now sets both class part of WM_CLASS property and instance +- `--class`'s `general` and `instance` options were swapped +- Search bar is now respecting cursor thickness +- On X11 the IME popup window is stuck at the bottom of the window due to Xlib limitations +- IME no longer works in Vi mode when moving around ### Fixed @@ -56,6 +68,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - On macOS, `font.use_thin_strokes` did not work since Big Sur - On macOS, trying to load a disabled font would crash - On macOS, Alacritty sessions did not appear in the list of tty sessions for `w` and `who` +- Cursor not hiding on GNOME Wayland +- Font having different scale factor after monitor powering off/on on X11 +- Viewport not updating after opening a new tabbed window on macOS +- Terminal not exiting sometimes after closing all windows on macOS +- CPU usage spikes due to mouse movements for unfocused windows on X11/Windows +- First window on macOS not tabbed with system prefer tabs setting +- Window being treaten as focused by default on Wayland ### Removed @@ -3,15 +3,16 @@ version = 3 [[package]] -name = "adler32" -version = "1.2.0" +name = "adler" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "alacritty" -version = "0.11.0-dev" +version = "0.12.0-dev" dependencies = [ + "alacritty_config", "alacritty_config_derive", "alacritty_terminal", "bitflags", @@ -31,24 +32,34 @@ dependencies = [ "notify", "objc", "once_cell", - "parking_lot", + "parking_lot 0.12.1", "png", - "raw-window-handle", + "raw-window-handle 0.5.0", "serde", "serde_json", "serde_yaml", "smallvec", "unicode-width", "wayland-client", - "winapi 0.3.9", + "windows-sys", "x11-dl", "xdg", ] [[package]] +name = "alacritty_config" +version = "0.1.1-dev" +dependencies = [ + "log", + "serde", + "serde_yaml", +] + +[[package]] name = "alacritty_config_derive" -version = "0.1.0" +version = "0.2.1-dev" dependencies = [ + "alacritty_config", "log", "proc-macro2", "quote", @@ -59,8 +70,9 @@ dependencies = [ [[package]] name = "alacritty_terminal" -version = "0.17.0-dev" +version = "0.17.1-dev" dependencies = [ + "alacritty_config", "alacritty_config_derive", "base64", "bitflags", @@ -72,7 +84,7 @@ dependencies = [ "mio-extras", "miow 0.3.7", "nix", - "parking_lot", + "parking_lot 0.12.1", "regex-automata", "serde", "serde_json", @@ -82,14 +94,20 @@ dependencies = [ "smallvec", "unicode-width", "vte", - "winapi 0.3.9", + "windows-sys", ] [[package]] -name = "android_glue" -version = "0.2.3" +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "atty" @@ -128,18 +146,27 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "bumpalo" -version = "3.10.0" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" + +[[package]] +name = "bytemuck" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" +checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da" [[package]] name = "calloop" -version = "0.9.3" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf2eec61efe56aa1e813f5126959296933cf0700030e4314786c48779a66ab82" +checksum = "a22a6a8f622f797120d452c630b0ab12e1331a1a753e2039ce7868d4ac77b4ee" dependencies = [ "log", "nix", + "slotmap", + "thiserror", + "vec_map", ] [[package]] @@ -171,9 +198,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.5" +version = "3.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53da17d37dba964b9b3ecb5c5a1f193a2762c700e6829201e645b9381c99dc7" +checksum = "1ed5341b2301a26ab80be5cbdced622e80ed808483c52e45e3310a877d3b37d7" dependencies = [ "atty", "bitflags", @@ -188,18 +215,18 @@ dependencies = [ [[package]] name = "clap_complete" -version = "3.2.1" +version = "3.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6ebaab5f25e4f0312dfa07cb30a755204b96e6531457c2cfdecfdf5f2adf40" +checksum = "3f7a2e0a962c45ce25afce14220bc24f9dade0a1787f185cecf96bfba7847cd8" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "3.2.5" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c11d40217d16aee8508cc8e5fde8b4ff24639758608e5374e731b53f85749fb9" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck", "proc-macro-error", @@ -210,9 +237,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ "os_str_bytes", ] @@ -245,8 +272,8 @@ dependencies = [ "bitflags", "block", "cocoa-foundation", - "core-foundation 0.9.3", - "core-graphics 0.22.3", + "core-foundation", + "core-graphics", "foreign-types 0.3.2", "libc", "objc", @@ -260,7 +287,7 @@ checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" dependencies = [ "bitflags", "block", - "core-foundation 0.9.3", + "core-foundation", "core-graphics-types", "foreign-types 0.3.2", "libc", @@ -283,56 +310,28 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" -dependencies = [ - "core-foundation-sys 0.7.0", - "libc", -] - -[[package]] -name = "core-foundation" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ - "core-foundation-sys 0.8.3", + "core-foundation-sys", "libc", ] [[package]] name = "core-foundation-sys" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" - -[[package]] -name = "core-foundation-sys" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "core-graphics" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" -dependencies = [ - "bitflags", - "core-foundation 0.7.0", - "foreign-types 0.3.2", - "libc", -] - -[[package]] -name = "core-graphics" version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" dependencies = [ "bitflags", - "core-foundation 0.9.3", + "core-foundation", "core-graphics-types", "foreign-types 0.3.2", "libc", @@ -345,7 +344,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" dependencies = [ "bitflags", - "core-foundation 0.9.3", + "core-foundation", "foreign-types 0.3.2", "libc", ] @@ -356,26 +355,13 @@ version = "19.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" dependencies = [ - "core-foundation 0.9.3", - "core-graphics 0.22.3", + "core-foundation", + "core-graphics", "foreign-types 0.3.2", "libc", ] [[package]] -name = "core-video-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828" -dependencies = [ - "cfg-if 0.1.10", - "core-foundation-sys 0.7.0", - "core-graphics 0.19.2", - "libc", - "objc", -] - -[[package]] name = "crc32fast" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -391,9 +377,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f66b1c1979c4362323f03ab6bf7fb522902bfc418e0c37319ab347f9561d980f" dependencies = [ "cocoa", - "core-foundation 0.9.3", - "core-foundation-sys 0.8.3", - "core-graphics 0.22.3", + "core-foundation", + "core-foundation-sys", + "core-graphics", "core-text", "dwrote", "foreign-types 0.5.0", @@ -505,9 +491,9 @@ dependencies = [ [[package]] name = "embed-resource" -version = "1.7.2" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc24ff8d764818e9ab17963b0593c535f077a513f565e75e4352d758bc4d8c0" +checksum = "936c1354206a875581696369aef920e12396e93bbd251c43a7a3f3fa85023a7d" dependencies = [ "cc", "rustc_version", @@ -528,14 +514,24 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c" +checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall", - "winapi 0.3.9", + "windows-sys", +] + +[[package]] +name = "flate2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +dependencies = [ + "crc32fast", + "miniz_oxide", ] [[package]] @@ -667,25 +663,24 @@ dependencies = [ [[package]] name = "glutin" -version = "0.28.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00ea9dbe544bc8a657c4c4a798c2d16cd01b549820e47657297549d28371f6d2" +checksum = "444c9ad294fdcaf20ccf6726b78f380b5450275540c9b68ab62f49726ad1c713" dependencies = [ - "android_glue", "cgl", "cocoa", - "core-foundation 0.9.3", + "core-foundation", "glutin_egl_sys", - "glutin_emscripten_sys", "glutin_gles2_sys", "glutin_glx_sys", "glutin_wgl_sys", - "lazy_static", "libloading", "log", "objc", + "once_cell", "osmesa-sys", - "parking_lot", + "parking_lot 0.12.1", + "raw-window-handle 0.5.0", "wayland-client", "wayland-egl", "winapi 0.3.9", @@ -694,21 +689,15 @@ dependencies = [ [[package]] name = "glutin_egl_sys" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2abb6aa55523480c4adc5a56bbaa249992e2dddb2fc63dc96e04a3355364c211" +checksum = "68900f84b471f31ea1d1355567eb865a2cf446294f06cef8d653ed7bcf5f013d" dependencies = [ "gl_generator", "winapi 0.3.9", ] [[package]] -name = "glutin_emscripten_sys" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80de4146df76e8a6c32b03007bc764ff3249dcaeb4f675d68a06caf1bac363f1" - -[[package]] name = "glutin_gles2_sys" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -720,9 +709,9 @@ dependencies = [ [[package]] name = "glutin_glx_sys" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e393c8fc02b807459410429150e9c4faffdb312d59b8c038566173c81991351" +checksum = "d93d0575865098580c5b3a423188cd959419912ea60b1e48e8b3b526f6d02468" dependencies = [ "gl_generator", "x11-dl", @@ -739,9 +728,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" @@ -766,9 +755,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c6392766afd7964e2531940894cffe4bd8d7d17dbc3c1c4857040fd4b33bdb3" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", "hashbrown", @@ -817,9 +806,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" [[package]] name = "jni-sys" @@ -829,9 +818,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.58" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] @@ -872,9 +861,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" [[package]] name = "libloading" @@ -888,15 +877,15 @@ dependencies = [ [[package]] name = "linked-hash-map" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "9f80bf5aacaf25cbfc8210d1cfb718f2bf3b11c4c54e5afe36c236853a8ec390" dependencies = [ "autocfg", "scopeguard", @@ -929,9 +918,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.3.1" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357" +checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498" dependencies = [ "libc", ] @@ -953,11 +942,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.3.7" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" dependencies = [ - "adler32", + "adler", ] [[package]] @@ -981,9 +970,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" dependencies = [ "libc", "log", @@ -999,7 +988,7 @@ checksum = "6bc513025fe5005a3aa561b50fdb2cda5a150b84800ae02acd8aa9ed62ca1a6b" dependencies = [ "mio 0.6.23", "miow 0.3.7", - "parking_lot", + "parking_lot 0.11.2", "spsc-buffer", "winapi 0.3.9", ] @@ -1050,14 +1039,15 @@ dependencies = [ [[package]] name = "ndk" -version = "0.5.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d868f654c72e75f8687572699cdabe755f03effbb62542768e995d5b8d699d" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" dependencies = [ "bitflags", "jni-sys", "ndk-sys", "num_enum", + "raw-window-handle 0.5.0", "thiserror", ] @@ -1069,17 +1059,18 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-glue" -version = "0.5.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71bee8ea72d685477e28bd004cfe1bf99c754d688cd78cad139eae4089484d4" +checksum = "0434fabdd2c15e0aab768ca31d5b7b333717f03cf02037d5a0a3ff3c278ed67f" dependencies = [ - "lazy_static", "libc", "log", "ndk", "ndk-context", "ndk-macro", "ndk-sys", + "once_cell", + "parking_lot 0.12.1", ] [[package]] @@ -1097,9 +1088,12 @@ dependencies = [ [[package]] name = "ndk-sys" -version = "0.2.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" +checksum = "21d83ec9c63ec5bf950200a8e508bdad6659972187b625469f58ef8c08e29046" +dependencies = [ + "jni-sys", +] [[package]] name = "net2" @@ -1114,12 +1108,11 @@ dependencies = [ [[package]] name = "nix" -version = "0.22.3" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" +checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" dependencies = [ "bitflags", - "cc", "cfg-if 1.0.0", "libc", "memoffset", @@ -1205,15 +1198,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.12.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" +checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" [[package]] name = "os_str_bytes" -version = "6.1.0" +version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] name = "osmesa-sys" @@ -1232,7 +1225,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core", + "parking_lot_core 0.8.5", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.3", ] [[package]] @@ -1250,10 +1253,23 @@ dependencies = [ ] [[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pkg-config" @@ -1263,21 +1279,23 @@ checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" [[package]] name = "png" -version = "0.16.8" +version = "0.17.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +checksum = "8f0e7f4c94ec26ff209cee506314212639d6c91b80afb82984819fafce9df01c" dependencies = [ "bitflags", "crc32fast", + "flate2", "miniz_oxide", ] [[package]] name = "proc-macro-crate" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" dependencies = [ + "once_cell", "thiserror", "toml", ] @@ -1308,9 +1326,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.39" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" dependencies = [ "unicode-ident", ] @@ -1326,9 +1344,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] @@ -1343,10 +1361,19 @@ dependencies = [ ] [[package]] +name = "raw-window-handle" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a" +dependencies = [ + "cty", +] + +[[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] @@ -1373,9 +1400,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.26" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "rustc_version" @@ -1388,9 +1415,18 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "safe_arch" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05" +dependencies = [ + "bytemuck", +] [[package]] name = "same-file" @@ -1414,25 +1450,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] +name = "sctk-adwaita" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04b7c47a572f73de28bee5b5060d085b42b6ce1e4ee2b49c956ea7b25e94b6f0" +dependencies = [ + "crossfont", + "log", + "smithay-client-toolkit", + "tiny-skia", +] + +[[package]] name = "semver" -version = "1.0.10" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41d061efea015927ac527063765e73601444cdc344ba855bc7bd44578b25e1c" +checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" [[package]] name = "serde" -version = "1.0.137" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.137" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" dependencies = [ "proc-macro2", "quote", @@ -1441,9 +1489,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.81" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" dependencies = [ "itoa", "ryu", @@ -1452,9 +1500,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.24" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707d15895415db6628332b737c838b88c598522e4dc70647e59b72312924aebc" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" dependencies = [ "indexmap", "ryu", @@ -1526,9 +1574,21 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slotmap" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +dependencies = [ + "version_check", +] [[package]] name = "smallvec" @@ -1541,9 +1601,9 @@ dependencies = [ [[package]] name = "smithay-client-toolkit" -version = "0.15.4" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a28f16a97fa0e8ce563b2774d1e732dd5d4025d2772c5dba0a41a0f90a29da3" +checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454" dependencies = [ "bitflags", "calloop", @@ -1560,9 +1620,9 @@ dependencies = [ [[package]] name = "smithay-clipboard" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "610b551bd25378bfd2b8e7a0fcbd83d427e8f2f6a40c47ae0f70688e9949dd55" +checksum = "0a345c870a1fae0b1b779085e81b51e614767c239e93503588e54c5b17f4b0e8" dependencies = [ "smithay-client-toolkit", "wayland-client", @@ -1582,9 +1642,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.96" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" dependencies = [ "proc-macro2", "quote", @@ -1602,24 +1662,24 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783" dependencies = [ "proc-macro2", "quote", @@ -1627,25 +1687,50 @@ dependencies = [ ] [[package]] +name = "tiny-skia" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "642680569bb895b16e4b9d181c60be1ed136fa0c9c7f11d004daf053ba89bf82" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if 1.0.0", + "png", + "safe_arch", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c114d32f0c2ee43d585367cb013dfaba967ab9f62b90d9af0d696e955e70fa6c" +dependencies = [ + "arrayref", + "bytemuck", +] + +[[package]] name = "toml" -version = "0.5.9" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" dependencies = [ "serde", ] [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "utf8parse" @@ -1654,6 +1739,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" [[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1718,9 +1809,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -1728,13 +1819,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -1743,9 +1834,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1753,9 +1844,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", @@ -1766,15 +1857,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "wayland-client" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91223460e73257f697d9e23d401279123d36039a3f7a449e983f123292d4458f" +checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" dependencies = [ "bitflags", "downcast-rs", @@ -1788,9 +1879,9 @@ dependencies = [ [[package]] name = "wayland-commons" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f6e5e340d7c13490eca867898c4cec5af56c27a5ffe5c80c6fc4708e22d33e" +checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" dependencies = [ "nix", "once_cell", @@ -1800,9 +1891,9 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c52758f13d5e7861fc83d942d3d99bf270c83269575e52ac29e5b73cb956a6bd" +checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" dependencies = [ "nix", "wayland-client", @@ -1811,9 +1902,9 @@ dependencies = [ [[package]] name = "wayland-egl" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83281d69ee162b59031c666385e93bde4039ec553b90c4191cdb128ceea29a3a" +checksum = "402de949f81a012926d821a2d659f930694257e76dd92b6e0042ceb27be4107d" dependencies = [ "wayland-client", "wayland-sys", @@ -1821,9 +1912,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60147ae23303402e41fe034f74fb2c35ad0780ee88a1c40ac09a3be1e7465741" +checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" dependencies = [ "bitflags", "wayland-client", @@ -1833,9 +1924,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39a1ed3143f7a143187156a2ab52742e89dac33245ba505c17224df48939f9e0" +checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" dependencies = [ "proc-macro2", "quote", @@ -1844,9 +1935,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9341df79a8975679188e37dab3889bfa57c44ac2cb6da166f519a81cbe452d4" +checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" dependencies = [ "dlib", "lazy_static", @@ -1855,9 +1946,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.58" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", @@ -1951,35 +2042,35 @@ checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "winit" -version = "0.26.1" +version = "0.27.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b43cc931d58b99461188607efd7acb2a093e65fc621f54cad78517a6063e73a" +checksum = "37f64802920c4c35d12a53dad5e0c55bbc3004d8dc4f2e4dd64ad02c5665d7aa" dependencies = [ "bitflags", "cocoa", - "core-foundation 0.9.3", - "core-graphics 0.22.3", - "core-video-sys", + "core-foundation", + "core-graphics", "dispatch", "instant", - "lazy_static", "libc", "log", - "mio 0.8.3", + "mio 0.8.4", "ndk", "ndk-glue", - "ndk-sys", "objc", - "parking_lot", + "once_cell", + "parking_lot 0.12.1", "percent-encoding", - "raw-window-handle", + "raw-window-handle 0.4.3", + "raw-window-handle 0.5.0", + "sctk-adwaita", "serde", "smithay-client-toolkit", "wasm-bindgen", "wayland-client", "wayland-protocols", "web-sys", - "winapi 0.3.9", + "windows-sys", "x11-dl", ] @@ -2022,9 +2113,9 @@ dependencies = [ [[package]] name = "x11-dl" -version = "2.19.1" +version = "2.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea26926b4ce81a6f5d9d0f3a0bc401e5a37c6ae14a1bfaa8ff6099ca80038c59" +checksum = "0c83627bc137605acc00bb399c7b908ef460b621fc37c953db2b09f88c449ea6" dependencies = [ "lazy_static", "libc", @@ -2,6 +2,7 @@ members = [ "alacritty", "alacritty_terminal", + "alacritty_config", "alacritty_config_derive", ] @@ -118,6 +118,17 @@ yum install cmake freetype-devel fontconfig-devel libxcb-devel libxkbcommon-deve yum group install "Development Tools" ``` +#### RHEL 8 + +On RHEL 8, like RHEL 7, you need a few extra libraries to build Alacritty. Here's a `dnf` +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 libxkbcommon-devel +dnf group install "Development Tools" +``` + #### openSUSE On openSUSE, you need a few extra libraries to build Alacritty. Here's diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 00000000..31aa7938 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/alacritty.yml b/alacritty.yml index 0f7bb884..4e9de637 100644 --- a/alacritty.yml +++ b/alacritty.yml @@ -94,11 +94,12 @@ # General application class #general: Alacritty - # GTK theme variant (Linux/BSD only) + # Decorations theme variant (Linux/BSD only) # - # Override the variant of the GTK theme. Commonly supported values are `dark` - # and `light`. Set this to `None` to use the default theme variant. - #gtk_theme_variant: None + # Override the variant of the GTK theme/Wayland client side decorations. + # Commonly supported values are `dark` and `light`. Set this to `None` to use + # the default theme variant. + #decorations_theme_variant: None #scrolling: # Maximum number of lines in the scrollback buffer. diff --git a/alacritty/Cargo.toml b/alacritty/Cargo.toml index d17b1a0e..f6aebfe1 100644 --- a/alacritty/Cargo.toml +++ b/alacritty/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "alacritty" -version = "0.11.0-dev" +version = "0.12.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" @@ -11,25 +11,29 @@ rust-version = "1.57.0" [dependencies.alacritty_terminal] path = "../alacritty_terminal" -version = "0.17.0-dev" +version = "0.17.1-dev" default-features = false [dependencies.alacritty_config_derive] path = "../alacritty_config_derive" -version = "0.1.0" +version = "0.2.1-dev" + +[dependencies.alacritty_config] +path = "../alacritty_config" +version = "0.1.1-dev" [dependencies] -clap = { version = "3.0.0", features = ["derive"] } +clap = { version = "3.0.0", features = ["derive", "env"] } log = { version = "0.4", features = ["std", "serde"] } fnv = "1" serde = { version = "1", features = ["derive"] } serde_yaml = "0.8" serde_json = "1" -glutin = { version = "0.28.0", default-features = false, features = ["serde"] } +glutin = { version = "0.29.1", default-features = false, features = ["serde"] } notify = "4" -parking_lot = "0.11.0" +parking_lot = "0.12.0" crossfont = { version = "0.5.0", features = ["force_system_fontconfig"] } -copypasta = { version = "0.8.0", default-features = false } +copypasta = { version = "0.8.1", default-features = false } libc = "0.2" unicode-width = "0.1" bitflags = "1" @@ -48,10 +52,10 @@ clap_complete = "3.0.0" xdg = "2.4.0" [target.'cfg(not(target_os = "macos"))'.dependencies] -png = { version = "0.16.8", default-features = false, optional = true } +png = { version = "0.17.5", default-features = false, optional = true } [target.'cfg(target_os = "macos")'.dependencies] -raw-window-handle = "0.4.0" +raw-window-handle = "0.5.0" cocoa = "0.24.0" objc = "0.2.2" @@ -60,13 +64,23 @@ x11-dl = { version = "2", optional = true } wayland-client = { version = "0.29.0", features = ["dlopen"], optional = true } [target.'cfg(windows)'.dependencies] -winapi = { version = "0.3.7", features = ["impl-default", "wincon"]} +windows-sys = { version = "0.36", features = [ + "Win32_UI_WindowsAndMessaging", + "Win32_System_Threading", + "Win32_System_Console", + "Win32_Foundation", +]} [target.'cfg(windows)'.build-dependencies] -embed-resource = "1.3" +embed-resource = "1.7.2" [features] default = ["wayland", "x11"] x11 = ["copypasta/x11", "glutin/x11", "x11-dl", "png"] -wayland = ["copypasta/wayland", "glutin/wayland", "glutin/wayland-dlopen", "wayland-client"] +wayland = [ + "copypasta/wayland", + "glutin/wayland", + "glutin/wayland-dlopen", + "glutin/wayland-csd-adwaita", + "wayland-client"] nightly = [] diff --git a/alacritty/alacritty.png b/alacritty/alacritty.png deleted file mode 120000 index 3865403a..00000000 --- a/alacritty/alacritty.png +++ /dev/null @@ -1 +0,0 @@ -../extra/logo/compat/alacritty-term.png
\ No newline at end of file diff --git a/alacritty/build.rs b/alacritty/build.rs index e8cf8e3b..99a7549b 100644 --- a/alacritty/build.rs +++ b/alacritty/build.rs @@ -13,7 +13,7 @@ fn main() { 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(); + let mut file = File::create(Path::new(&dest).join("gl_bindings.rs")).unwrap(); let extensions = ["GL_ARB_blend_func_extended", "GL_ARB_clear_texture"]; Registry::new(Api::Gl, (3, 3), Profile::Core, Fallbacks::All, extensions) @@ -21,12 +21,12 @@ fn main() { .unwrap(); #[cfg(windows)] - embed_resource::compile("./windows/windows.rc"); + embed_resource::compile("./windows/alacritty.rc"); } fn commit_hash() -> Option<String> { Command::new("git") - .args(&["rev-parse", "--short", "HEAD"]) + .args(["rev-parse", "--short", "HEAD"]) .output() .ok() .filter(|output| output.status.success()) diff --git a/alacritty/extra b/alacritty/extra new file mode 120000 index 00000000..858d6fa4 --- /dev/null +++ b/alacritty/extra @@ -0,0 +1 @@ +../extra
\ No newline at end of file diff --git a/alacritty/src/cli.rs b/alacritty/src/cli.rs index 8872ec99..e7aae207 100644 --- a/alacritty/src/cli.rs +++ b/alacritty/src/cli.rs @@ -11,7 +11,7 @@ use serde_yaml::Value; use alacritty_terminal::config::{Program, PtyConfig}; -use crate::config::window::{Class, Identity, DEFAULT_NAME}; +use crate::config::window::{Class, Identity}; use crate::config::{serde_utils, UiConfig}; /// CLI options for the main Alacritty executable. @@ -81,14 +81,7 @@ impl Options { let mut options = Self::parse(); // 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.config_options = options_as_value(&options.option); options } @@ -132,7 +125,18 @@ impl Options { } } -/// Format an option in the format of `parent.field=value` to a serde Value. +/// Combine multiple options into a [`serde_yaml::Value`]. +pub fn options_as_value(options: &[String]) -> Value { + options.iter().fold(Value::default(), |value, option| match option_as_value(option) { + Ok(new_value) => serde_utils::merge(value, new_value), + Err(_) => { + eprintln!("Ignoring invalid option: {:?}", option); + value + }, + }) +} + +/// Parse an option in the format of `parent.field=value` as a serde Value. fn option_as_value(option: &str) -> Result<Value, serde_yaml::Error> { let mut yaml_text = String::with_capacity(option.len()); let mut closing_brackets = String::new(); @@ -159,19 +163,16 @@ fn option_as_value(option: &str) -> Result<Value, serde_yaml::Error> { /// 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 }) + let (general, instance) = match input.split_once(',') { + // Warn the user if they've passed too many values. + Some((_, instance)) if instance.contains(',') => { + return Err(String::from("Too many parameters")) }, - None => Ok(Class { instance: input.into(), general: DEFAULT_NAME.into() }), - } + Some((general, instance)) => (general, instance), + None => (input, input), + }; + + Ok(Class::new(general, instance)) } /// Convert to hex if possible, else decimal @@ -269,7 +270,7 @@ pub enum Subcommands { #[derive(Args, Debug)] pub struct MessageOptions { /// IPC socket connection path override. - #[clap(long, short, value_hint = ValueHint::FilePath)] + #[clap(short, long, value_hint = ValueHint::FilePath)] pub socket: Option<PathBuf>, /// Message which should be sent. @@ -283,9 +284,12 @@ pub struct MessageOptions { pub enum SocketMessage { /// Create a new window in the same Alacritty process. CreateWindow(WindowOptions), + + /// Update the Alacritty configuration. + Config(IpcConfig), } -/// Subset of options that we pass to a 'create-window' subcommand. +/// Subset of options that we pass to 'create-window' IPC subcommand. #[derive(Serialize, Deserialize, Args, Default, Clone, Debug, PartialEq, Eq)] pub struct WindowOptions { /// Terminal options which can be passed via IPC. @@ -297,6 +301,25 @@ pub struct WindowOptions { pub window_identity: WindowIdentity, } +/// Parameters to the `config` IPC subcommand. +#[cfg(unix)] +#[derive(Args, Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)] +pub struct IpcConfig { + /// Configuration file options [example: cursor.style=Beam]. + #[clap(required = true, value_name = "CONFIG_OPTIONS")] + pub options: Vec<String>, + + /// Window ID for the new config. + /// + /// Use `-1` to apply this change to all windows. + #[clap(short, long, allow_hyphen_values = true, env = "ALACRITTY_WINDOW_ID")] + pub window_id: Option<i128>, + + /// Clear all runtime configuration changes. + #[clap(short, long, conflicts_with = "options")] + pub reset: bool, +} + #[cfg(test)] mod tests { use super::*; @@ -385,15 +408,15 @@ mod tests { #[test] fn parse_instance_class() { let class = parse_class("one").unwrap(); + assert_eq!(class.general, "one"); 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"); + assert_eq!(class.general, "one"); + assert_eq!(class.instance, "two"); } #[test] diff --git a/alacritty/src/config/bindings.rs b/alacritty/src/config/bindings.rs index c48b2d75..3aae25ed 100644 --- a/alacritty/src/config/bindings.rs +++ b/alacritty/src/config/bindings.rs @@ -9,7 +9,7 @@ use serde::de::{self, Error as SerdeError, MapAccess, Unexpected, Visitor}; use serde::{Deserialize, Deserializer}; use serde_yaml::Value as SerdeValue; -use alacritty_config_derive::ConfigDeserialize; +use alacritty_config_derive::{ConfigDeserialize, SerdeReplace}; use alacritty_terminal::config::Program; use alacritty_terminal::term::TermMode; @@ -899,7 +899,7 @@ struct RawBinding { } impl RawBinding { - fn into_mouse_binding(self) -> Result<MouseBinding, Self> { + fn into_mouse_binding(self) -> Result<MouseBinding, Box<Self>> { if let Some(mouse) = self.mouse { Ok(Binding { trigger: mouse, @@ -909,11 +909,11 @@ impl RawBinding { notmode: self.notmode, }) } else { - Err(self) + Err(Box::new(self)) } } - fn into_key_binding(self) -> Result<KeyBinding, Self> { + fn into_key_binding(self) -> Result<KeyBinding, Box<Self>> { if let Some(key) = self.key { Ok(KeyBinding { trigger: key, @@ -923,7 +923,7 @@ impl RawBinding { notmode: self.notmode, }) } else { - Err(self) + Err(Box::new(self)) } } } @@ -1191,7 +1191,7 @@ impl<'a> Deserialize<'a> for KeyBinding { /// /// Our deserialize impl wouldn't be covered by a derive(Deserialize); see the /// impl below. -#[derive(Debug, Copy, Clone, Hash, Default, Eq, PartialEq)] +#[derive(SerdeReplace, Debug, Copy, Clone, Hash, Default, Eq, PartialEq)] pub struct ModsWrapper(pub ModifiersState); impl ModsWrapper { diff --git a/alacritty/src/config/font.rs b/alacritty/src/config/font.rs index d3431171..9c431b15 100644 --- a/alacritty/src/config/font.rs +++ b/alacritty/src/config/font.rs @@ -4,7 +4,7 @@ use crossfont::Size as FontSize; use serde::de::{self, Visitor}; use serde::{Deserialize, Deserializer}; -use alacritty_config_derive::ConfigDeserialize; +use alacritty_config_derive::{ConfigDeserialize, SerdeReplace}; use crate::config::ui_config::Delta; @@ -129,7 +129,7 @@ impl SecondaryFontDescription { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(SerdeReplace, Debug, Clone, PartialEq, Eq)] struct Size(FontSize); impl Default for Size { diff --git a/alacritty/src/config/monitor.rs b/alacritty/src/config/monitor.rs index 305f5dfb..8981570c 100644 --- a/alacritty/src/config/monitor.rs +++ b/alacritty/src/config/monitor.rs @@ -63,7 +63,7 @@ pub fn watch(mut paths: Vec<PathBuf>, event_proxy: EventLoopProxy<Event>) { // Watch all configuration file directories. for parent in &parents { - if let Err(err) = watcher.watch(&parent, RecursiveMode::NonRecursive) { + if let Err(err) = watcher.watch(parent, RecursiveMode::NonRecursive) { debug!("Unable to watch config directory {:?}: {}", parent, err); } } diff --git a/alacritty/src/config/ui_config.rs b/alacritty/src/config/ui_config.rs index 28139d27..a332b737 100644 --- a/alacritty/src/config/ui_config.rs +++ b/alacritty/src/config/ui_config.rs @@ -9,7 +9,7 @@ use serde::de::{Error as SerdeError, MapAccess, Visitor}; use serde::{self, Deserialize, Deserializer}; use unicode_width::UnicodeWidthChar; -use alacritty_config_derive::ConfigDeserialize; +use alacritty_config_derive::{ConfigDeserialize, SerdeReplace}; use alacritty_terminal::config::{ Config as TerminalConfig, Percentage, Program, LOG_TARGET_CONFIG, }; @@ -30,7 +30,7 @@ use crate::config::window::WindowConfig; const URL_REGEX: &str = "(ipfs:|ipns:|magnet:|mailto:|gemini:|gopher:|https:|http:|news:|file:|git:|ssh:|ftp:)\ [^\u{0000}-\u{001F}\u{007F}-\u{009F}<>\"\\s{-}\\^⟨⟩`]+"; -#[derive(ConfigDeserialize, Debug, PartialEq)] +#[derive(ConfigDeserialize, Clone, Debug, PartialEq)] pub struct UiConfig { /// Font configuration. pub font: Font, @@ -145,7 +145,7 @@ impl UiConfig { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(SerdeReplace, Clone, Debug, PartialEq, Eq)] struct KeyBindings(Vec<KeyBinding>); impl Default for KeyBindings { @@ -163,7 +163,7 @@ impl<'de> Deserialize<'de> for KeyBindings { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(SerdeReplace, Clone, Debug, PartialEq, Eq)] struct MouseBindings(Vec<MouseBinding>); impl Default for MouseBindings { @@ -223,7 +223,7 @@ pub struct Delta<T: Default> { } /// Regex terminal hints. -#[derive(ConfigDeserialize, Debug, PartialEq, Eq)] +#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)] pub struct Hints { /// Characters for the hint labels. alphabet: HintsAlphabet, @@ -273,7 +273,7 @@ impl Hints { } } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(SerdeReplace, Clone, Debug, PartialEq, Eq)] struct HintsAlphabet(String); impl Default for HintsAlphabet { diff --git a/alacritty/src/config/window.rs b/alacritty/src/config/window.rs index 08f38b57..5d63d60f 100644 --- a/alacritty/src/config/window.rs +++ b/alacritty/src/config/window.rs @@ -6,7 +6,7 @@ use log::{error, warn}; use serde::de::{self, MapAccess, Visitor}; use serde::{Deserialize, Deserializer, Serialize}; -use alacritty_config_derive::ConfigDeserialize; +use alacritty_config_derive::{ConfigDeserialize, SerdeReplace}; use alacritty_terminal::config::{Percentage, LOG_TARGET_CONFIG}; use alacritty_terminal::index::Column; @@ -31,7 +31,13 @@ pub struct WindowConfig { pub embed: Option<c_ulong>, /// GTK theme variant. - pub gtk_theme_variant: Option<String>, + #[config(deprecated = "use window.decorations_theme_variant instead")] + gtk_theme_variant: Option<String>, + + /// System decorations theme variant. + /// + /// Controls GTK theme variant on X11 and winit client side decorations on Wayland. + decorations_theme_variant: Option<String>, /// Spread out additional padding evenly. pub dynamic_padding: bool, @@ -61,6 +67,7 @@ impl Default for WindowConfig { decorations: Default::default(), startup_mode: Default::default(), embed: Default::default(), + decorations_theme_variant: Default::default(), gtk_theme_variant: Default::default(), dynamic_padding: Default::default(), identity: Identity::default(), @@ -104,6 +111,15 @@ impl WindowConfig { } } + #[cfg(not(any(target_os = "macos", windows)))] + #[inline] + pub fn decorations_theme_variant(&self) -> Option<&str> { + self.gtk_theme_variant + .as_ref() + .or_else(|| self.decorations_theme_variant.as_ref()) + .map(|theme| theme.as_str()) + } + #[inline] pub fn padding(&self, scale_factor: f64) -> (f32, f32) { let padding_x = (f32::from(self.padding.x) * scale_factor as f32).floor(); @@ -185,15 +201,21 @@ pub struct Dimensions { } /// Window class hint. -#[derive(Serialize, Debug, Clone, PartialEq, Eq)] +#[derive(SerdeReplace, Serialize, Debug, Clone, PartialEq, Eq)] pub struct Class { - pub instance: String, pub general: String, + pub instance: String, +} + +impl Class { + pub fn new(general: impl ToString, instance: impl ToString) -> Self { + Self { general: general.to_string(), instance: instance.to_string() } + } } impl Default for Class { fn default() -> Self { - Self { instance: DEFAULT_NAME.into(), general: DEFAULT_NAME.into() } + Self::new(DEFAULT_NAME, DEFAULT_NAME) } } diff --git a/alacritty/src/daemon.rs b/alacritty/src/daemon.rs index 730a2cc7..df66646a 100644 --- a/alacritty/src/daemon.rs +++ b/alacritty/src/daemon.rs @@ -18,7 +18,7 @@ use { #[cfg(not(windows))] use libc::pid_t; #[cfg(windows)] -use winapi::um::winbase::{CREATE_NEW_PROCESS_GROUP, CREATE_NO_WINDOW}; +use windows_sys::Win32::System::Threading::{CREATE_NEW_PROCESS_GROUP, CREATE_NO_WINDOW}; #[cfg(target_os = "macos")] use crate::macos; diff --git a/alacritty/src/display/content.rs b/alacritty/src/display/content.rs index 82644018..fa59bc13 100644 --- a/alacritty/src/display/content.rs +++ b/alacritty/src/display/content.rs @@ -54,6 +54,7 @@ impl<'a> RenderableContent<'a> { let cursor_shape = if terminal_content.cursor.shape == CursorShape::Hidden || display.cursor_hidden || search_state.regex().is_some() + || display.ime.preedit().is_some() { CursorShape::Hidden } else if !term.is_focused && config.terminal_config.cursor.unfocused_hollow { @@ -423,6 +424,10 @@ impl RenderableCursor { } impl RenderableCursor { + pub fn new(point: Point<usize>, shape: CursorShape, cursor_color: Rgb, is_wide: bool) -> Self { + Self { shape, cursor_color, text_color: cursor_color, is_wide, point } + } + pub fn color(&self) -> Rgb { self.cursor_color } diff --git a/alacritty/src/display/cursor.rs b/alacritty/src/display/cursor.rs index 89b0bcde..8a4cc729 100644 --- a/alacritty/src/display/cursor.rs +++ b/alacritty/src/display/cursor.rs @@ -22,7 +22,7 @@ impl IntoRects for RenderableCursor { let mut width = size_info.cell_width(); let height = size_info.cell_height(); - let thickness = (thickness * width as f32).round().max(1.); + let thickness = (thickness * width).round().max(1.); if self.is_wide() { width *= 2.; diff --git a/alacritty/src/display/mod.rs b/alacritty/src/display/mod.rs index c731314e..f5c10de4 100644 --- a/alacritty/src/display/mod.rs +++ b/alacritty/src/display/mod.rs @@ -20,8 +20,9 @@ use serde::{Deserialize, Serialize}; use wayland_client::EventQueue; use crossfont::{self, Rasterize, Rasterizer}; +use unicode_width::UnicodeWidthChar; -use alacritty_terminal::ansi::NamedColor; +use alacritty_terminal::ansi::{CursorShape, NamedColor}; use alacritty_terminal::config::MAX_SCROLLBACK_LINES; use alacritty_terminal::event::{EventListener, OnResize, WindowSize}; use alacritty_terminal::grid::Dimensions as TermDimensions; @@ -38,7 +39,7 @@ use crate::config::window::{Dimensions, Identity}; use crate::config::UiConfig; use crate::display::bell::VisualBell; use crate::display::color::List; -use crate::display::content::RenderableContent; +use crate::display::content::{RenderableContent, RenderableCursor}; use crate::display::cursor::IntoRects; use crate::display::damage::RenderDamageIterator; use crate::display::hint::{HintMatch, HintState}; @@ -46,7 +47,7 @@ use crate::display::meter::Meter; use crate::display::window::Window; use crate::event::{Mouse, SearchState}; use crate::message_bar::{MessageBuffer, MessageType}; -use crate::renderer::rects::{RenderLines, RenderRect}; +use crate::renderer::rects::{RenderLine, RenderLines, RenderRect}; use crate::renderer::{self, GlyphCache, Renderer}; use crate::string::{ShortenDirection, StrShortener}; @@ -372,6 +373,9 @@ pub struct Display { /// The renderer update that takes place only once before the actual rendering. pub pending_renderer_update: Option<RendererUpdate>, + /// The ime on the given display. + pub ime: Ime, + // Mouse point position when highlighting hints. hint_mouse_point: Option<Point>, @@ -384,6 +388,77 @@ pub struct Display { meter: Meter, } +/// Input method state. +#[derive(Debug, Default)] +pub struct Ime { + /// Whether the IME is enabled. + enabled: bool, + + /// Current IME preedit. + preedit: Option<Preedit>, +} + +impl Ime { + pub fn new() -> Self { + Default::default() + } + + #[inline] + pub fn set_enabled(&mut self, is_enabled: bool) { + if is_enabled { + self.enabled = is_enabled + } else { + // Clear state when disabling IME. + *self = Default::default(); + } + } + + #[inline] + pub fn is_enabled(&self) -> bool { + self.enabled + } + + #[inline] + pub fn set_preedit(&mut self, preedit: Option<Preedit>) { + self.preedit = preedit; + } + + #[inline] + pub fn preedit(&self) -> Option<&Preedit> { + self.preedit.as_ref() + } +} + +#[derive(Debug, Default, PartialEq, Eq)] +pub struct Preedit { + /// The preedit text. + text: String, + + /// Byte offset for cursor start into the preedit text. + /// + /// `None` means that the cursor is invisible. + cursor_byte_offset: Option<usize>, + + /// The cursor offset from the end of the preedit in char width. + cursor_end_offset: Option<usize>, +} + +impl Preedit { + pub fn new(text: String, cursor_byte_offset: Option<usize>) -> Self { + let cursor_end_offset = if let Some(byte_offset) = cursor_byte_offset { + // Convert byte offset into char offset. + let cursor_end_offset = + text[byte_offset..].chars().fold(0, |acc, ch| acc + ch.width().unwrap_or(1)); + + Some(cursor_end_offset) + } else { + None + }; + + Self { text, cursor_byte_offset, cursor_end_offset } + } +} + /// Pending renderer updates. /// /// All renderer updates are cached to be applied just before rendering, to avoid platform-specific @@ -539,6 +614,7 @@ impl Display { hint_state, meter: Meter::new(), size_info, + ime: Ime::new(), highlighted_hint: None, vi_highlighted_hint: None, #[cfg(not(any(target_os = "macos", windows)))] @@ -639,7 +715,7 @@ impl Display { // Update number of column/lines in the viewport. let message_bar_lines = message_buffer.message().map_or(0, |m| m.text(&self.size_info).len()); - let search_lines = if search_active { 1 } else { 0 }; + let search_lines = usize::from(search_active); self.size_info.reserve_lines(message_bar_lines + search_lines); // Resize PTY. @@ -760,6 +836,7 @@ impl Display { grid_cells.push(cell); } let selection_range = content.selection_range(); + let foreground_color = content.color(NamedColor::Foreground as usize); let background_color = content.color(NamedColor::Background as usize); let display_offset = content.display_offset(); let cursor = content.cursor(); @@ -860,9 +937,7 @@ impl Display { }; // Draw cursor. - for rect in cursor.rects(&size_info, config.terminal_config.cursor.thickness()) { - rects.push(rect); - } + rects.extend(cursor.rects(&size_info, config.terminal_config.cursor.thickness())); // Push visual bell after url/underline/strikeout rects. let visual_bell_intensity = self.visual_bell.intensity(); @@ -878,12 +953,61 @@ impl Display { rects.push(visual_bell_rect); } + // Handle IME positioning and search bar rendering. + let ime_position = match search_state.regex() { + Some(regex) => { + let search_label = match search_state.direction() { + Direction::Right => FORWARD_SEARCH_LABEL, + Direction::Left => BACKWARD_SEARCH_LABEL, + }; + + let search_text = Self::format_search(regex, search_label, size_info.columns()); + + // Render the search bar. + self.draw_search(config, &search_text); + + // Draw search bar cursor. + let line = size_info.screen_lines(); + let column = Column(search_text.chars().count() - 1); + + // Add cursor to search bar if IME is not active. + if self.ime.preedit().is_none() { + let fg = config.colors.footer_bar_foreground(); + let shape = CursorShape::Underline; + let cursor = RenderableCursor::new(Point::new(line, column), shape, fg, false); + rects.extend( + cursor.rects(&size_info, config.terminal_config.cursor.thickness()), + ); + } + + Some(Point::new(line, column)) + }, + None => { + let num_lines = self.size_info.screen_lines(); + term::point_to_viewport(display_offset, cursor_point) + .filter(|point| point.line < num_lines) + }, + }; + + // Handle IME. + if self.ime.is_enabled() { + if let Some(point) = ime_position { + let (fg, bg) = if search_state.regex().is_some() { + (config.colors.footer_bar_foreground(), config.colors.footer_bar_background()) + } else { + (foreground_color, background_color) + }; + + self.draw_ime_preview(point, fg, bg, &mut rects, config); + } + } + if self.debug_damage { self.highlight_damage(&mut rects); } if let Some(message) = message_buffer.message() { - let search_offset = if search_state.regex().is_some() { 1 } else { 0 }; + let search_offset = usize::from(search_state.regex().is_some()); let text = message.text(&size_info); // Create a new rectangle for the background. @@ -925,34 +1049,12 @@ impl Display { self.draw_render_timer(config); - // Handle search and IME positioning. - let ime_position = match search_state.regex() { - Some(regex) => { - let search_label = match search_state.direction() { - Direction::Right => FORWARD_SEARCH_LABEL, - Direction::Left => BACKWARD_SEARCH_LABEL, - }; - - let search_text = Self::format_search(regex, search_label, size_info.columns()); - - // Render the search bar. - self.draw_search(config, &search_text); - - // Compute IME position. - let line = Line(size_info.screen_lines() as i32 + 1); - Point::new(line, Column(search_text.chars().count() - 1)) - }, - None => cursor_point, - }; - // Draw hyperlink uri preview. if has_highlighted_hint { - self.draw_hyperlink_preview(config, vi_cursor_point, display_offset); + let cursor_point = vi_cursor_point.or(Some(cursor_point)); + self.draw_hyperlink_preview(config, cursor_point, display_offset); } - // Update IME position. - self.window.update_ime_position(ime_position, &self.size_info); - // Frame event should be requested before swaping buffers, since it requires surface // `commit`, which is done by swap buffers under the hood. #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] @@ -1040,6 +1142,95 @@ impl Display { dirty } + #[inline(never)] + fn draw_ime_preview( + &mut self, + point: Point<usize>, + fg: Rgb, + bg: Rgb, + rects: &mut Vec<RenderRect>, + config: &UiConfig, + ) { + let preedit = match self.ime.preedit() { + Some(preedit) => preedit, + None => { + // In case we don't have preedit, just set the popup point. + self.window.update_ime_position(point, &self.size_info); + return; + }, + }; + + let num_cols = self.size_info.columns(); + + // Get the visible preedit. + let visible_text: String = match (preedit.cursor_byte_offset, preedit.cursor_end_offset) { + (Some(byte_offset), Some(end_offset)) if end_offset > num_cols => StrShortener::new( + &preedit.text[byte_offset..], + num_cols, + ShortenDirection::Right, + Some(SHORTENER), + ), + _ => { + StrShortener::new(&preedit.text, num_cols, ShortenDirection::Left, Some(SHORTENER)) + }, + } + .collect(); + + let visible_len = visible_text.chars().count(); + + let end = cmp::min(point.column.0 + visible_len, num_cols); + let start = end.saturating_sub(visible_len); + + let start = Point::new(point.line, Column(start)); + let end = Point::new(point.line, Column(end - 1)); + + let glyph_cache = &mut self.glyph_cache; + let metrics = glyph_cache.font_metrics(); + + self.renderer.draw_string( + start, + fg, + bg, + visible_text.chars(), + &self.size_info, + glyph_cache, + ); + + if self.collect_damage() { + let damage = self.damage_from_point(Point::new(start.line, Column(0)), num_cols as u32); + self.damage_rects.push(damage); + self.next_frame_damage_rects.push(damage); + } + + // Add underline for preedit text. + let underline = RenderLine { start, end, color: fg }; + rects.extend(underline.rects(Flags::UNDERLINE, &metrics, &self.size_info)); + + let ime_popup_point = match preedit.cursor_end_offset { + Some(cursor_end_offset) if cursor_end_offset != 0 => { + let is_wide = preedit.text[preedit.cursor_byte_offset.unwrap_or_default()..] + .chars() + .next() + .map(|ch| ch.width() == Some(2)) + .unwrap_or_default(); + + let cursor_column = Column( + (end.column.0 as isize - cursor_end_offset as isize + 1).max(0) as usize, + ); + let cursor_point = Point::new(point.line, cursor_column); + let cursor = + RenderableCursor::new(cursor_point, CursorShape::HollowBlock, fg, is_wide); + rects.extend( + cursor.rects(&self.size_info, config.terminal_config.cursor.thickness()), + ); + cursor_point + }, + _ => end, + }; + + self.window.update_ime_position(ime_popup_point, &self.size_info); + } + /// Format search regex to account for the cursor and fullwidth characters. fn format_search(search_regex: &str, search_label: &str, max_width: usize) -> String { let label_len = search_label.len(); @@ -1058,7 +1249,8 @@ impl Display { Some(SHORTENER), )); - bar_text.push('_'); + // Add place for cursor. + bar_text.push(' '); bar_text } @@ -1068,7 +1260,7 @@ impl Display { fn draw_hyperlink_preview( &mut self, config: &UiConfig, - vi_cursor_point: Option<Point>, + cursor_point: Option<Point>, display_offset: usize, ) { let num_cols = self.size_info.columns(); @@ -1094,7 +1286,7 @@ impl Display { // Prefer to show preview even when it'll likely obscure the highlighted hint, when // there's no place left for it. protected_lines.push(self.hint_mouse_point.map(|point| point.line)); - protected_lines.push(vi_cursor_point.map(|point| point.line)); + protected_lines.push(cursor_point.map(|point| point.line)); } // Find the line in viewport we can draw preview on without obscuring protected lines. @@ -1229,7 +1421,7 @@ impl Display { let x = size_info.padding_x() + point.column.0 as u32 * size_info.cell_width(); let y_top = size_info.height() - size_info.padding_y(); let y = y_top - (point.line as u32 + 1) * size_info.cell_height(); - let width = len as u32 * size_info.cell_width(); + let width = len * size_info.cell_width(); DamageRect { x, y, width, height: size_info.cell_height() } } diff --git a/alacritty/src/display/window.rs b/alacritty/src/display/window.rs index 480654cf..f558a654 100644 --- a/alacritty/src/display/window.rs +++ b/alacritty/src/display/window.rs @@ -13,6 +13,7 @@ use { wayland_client::protocol::wl_surface::WlSurface, wayland_client::{Attached, EventQueue, Proxy}, glutin::platform::unix::EventLoopWindowTargetExtUnix, + glutin::window::Theme, }; #[rustfmt::skip] @@ -46,8 +47,6 @@ use glutin::{self, ContextBuilder, PossiblyCurrent, Rect, WindowedContext}; use objc::{msg_send, sel, sel_impl}; #[cfg(target_os = "macos")] use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; -#[cfg(windows)] -use winapi::shared::minwindef::WORD; use alacritty_terminal::index::Point; @@ -58,11 +57,11 @@ use crate::gl; /// Window icon for `_NET_WM_ICON` property. #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] -static WINDOW_ICON: &[u8] = include_bytes!("../../alacritty.png"); +static WINDOW_ICON: &[u8] = include_bytes!("../../extra/logo/compat/alacritty-term.png"); -/// This should match the definition of IDI_ICON from `windows.rc`. +/// This should match the definition of IDI_ICON from `alacritty.rc`. #[cfg(windows)] -const IDI_ICON: WORD = 0x101; +const IDI_ICON: u16 = 0x101; /// Context creation flags from probing config. static GL_CONTEXT_CREATION_FLAGS: AtomicU8 = AtomicU8::new(GlContextFlags::SRGB.bits); @@ -233,6 +232,9 @@ impl Window { let current_mouse_cursor = CursorIcon::Text; windowed_context.window().set_cursor_icon(current_mouse_cursor); + // Enable IME. + windowed_context.window().set_ime_allowed(true); + // Set OpenGL symbol loader. This call MUST be after window.make_current on windows. gl::load_with(|symbol| windowed_context.get_proc_address(symbol) as *const _); @@ -322,15 +324,18 @@ impl Window { pub fn get_platform_window(identity: &Identity, window_config: &WindowConfig) -> WindowBuilder { #[cfg(feature = "x11")] let icon = { - let decoder = Decoder::new(Cursor::new(WINDOW_ICON)); - let (info, mut reader) = decoder.read_info().expect("invalid embedded icon"); - let mut buf = vec![0; info.buffer_size()]; + let mut decoder = Decoder::new(Cursor::new(WINDOW_ICON)); + decoder.set_transformations(png::Transformations::normalize_to_color8()); + let mut reader = decoder.read_info().expect("invalid embedded icon"); + let mut buf = vec![0; reader.output_buffer_size()]; let _ = reader.next_frame(&mut buf); - Icon::from_rgba(buf, info.width, info.height) + Icon::from_rgba(buf, reader.info().width, reader.info().height) + .expect("invalid embedded icon format") }; let builder = WindowBuilder::new() .with_title(&identity.title) + .with_name(&identity.class.general, &identity.class.instance) .with_visible(false) .with_transparent(true) .with_decorations(window_config.decorations != Decorations::None) @@ -338,21 +343,21 @@ impl Window { .with_fullscreen(window_config.fullscreen()); #[cfg(feature = "x11")] - let builder = builder.with_window_icon(icon.ok()); - - #[cfg(feature = "wayland")] - let builder = builder.with_app_id(identity.class.instance.to_owned()); - - #[cfg(feature = "x11")] - let builder = builder - .with_class(identity.class.instance.to_owned(), identity.class.general.to_owned()); + let builder = builder.with_window_icon(Some(icon)); #[cfg(feature = "x11")] - let builder = match &window_config.gtk_theme_variant { - Some(val) => builder.with_gtk_theme_variant(val.clone()), + let builder = match window_config.decorations_theme_variant() { + Some(val) => builder.with_gtk_theme_variant(val.to_string()), None => builder, }; + #[cfg(feature = "wayland")] + let builder = match window_config.decorations_theme_variant() { + Some("light") => builder.with_wayland_csd_theme(Theme::Light), + // Prefer dark theme by default, since default alacritty theme is dark. + _ => builder.with_wayland_csd_theme(Theme::Dark), + }; + builder } @@ -400,16 +405,6 @@ impl Window { self.window().request_user_attention(attention); } - #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] - pub fn x11_window_id(&self) -> Option<usize> { - self.window().xlib_window().map(|xlib_window| xlib_window as usize) - } - - #[cfg(any(not(feature = "x11"), target_os = "macos", windows))] - pub fn x11_window_id(&self) -> Option<usize> { - None - } - pub fn id(&self) -> WindowId { self.window().id() } @@ -455,10 +450,14 @@ impl Window { self.wayland_surface.as_ref() } + pub fn set_ime_allowed(&self, allowed: bool) { + self.windowed_context.window().set_ime_allowed(allowed); + } + /// Adjust the IME editor position according to the new location of the cursor. - pub fn update_ime_position(&self, point: Point, size: &SizeInfo) { + pub fn update_ime_position(&self, point: Point<usize>, size: &SizeInfo) { let nspot_x = f64::from(size.padding_x() + point.column.0 as f32 * size.cell_width()); - let nspot_y = f64::from(size.padding_y() + (point.line.0 + 1) as f32 * size.cell_height()); + let nspot_y = f64::from(size.padding_y() + (point.line + 1) as f32 * size.cell_height()); self.window().set_ime_position(PhysicalPosition::new(nspot_x, nspot_y)); } diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs index cfa1c25c..853e3f5e 100644 --- a/alacritty/src/event.rs +++ b/alacritty/src/event.rs @@ -9,12 +9,17 @@ use std::fmt::Debug; #[cfg(not(windows))] use std::os::unix::io::RawFd; use std::path::PathBuf; +use std::rc::Rc; use std::time::{Duration, Instant}; use std::{env, f32, mem}; use glutin::dpi::PhysicalSize; -use glutin::event::{ElementState, Event as GlutinEvent, ModifiersState, MouseButton, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop, EventLoopProxy, EventLoopWindowTarget}; +use glutin::event::{ + ElementState, Event as GlutinEvent, Ime, ModifiersState, MouseButton, StartCause, WindowEvent, +}; +use glutin::event_loop::{ + ControlFlow, DeviceEventFilter, EventLoop, EventLoopProxy, EventLoopWindowTarget, +}; use glutin::platform::run_return::EventLoopExtRunReturn; #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] use glutin::platform::unix::EventLoopWindowTargetExtUnix; @@ -34,6 +39,8 @@ use alacritty_terminal::selection::{Selection, SelectionType}; use alacritty_terminal::term::search::{Match, RegexSearch}; use alacritty_terminal::term::{self, ClipboardType, Term, TermMode}; +#[cfg(unix)] +use crate::cli::IpcConfig; use crate::cli::{Options as CliOptions, WindowOptions}; use crate::clipboard::Clipboard; use crate::config::ui_config::{HintAction, HintInternalAction}; @@ -43,7 +50,7 @@ use crate::daemon::foreground_process_path; use crate::daemon::spawn_daemon; use crate::display::hint::HintMatch; use crate::display::window::Window; -use crate::display::{Display, SizeInfo}; +use crate::display::{Display, Preedit, SizeInfo}; use crate::input::{self, ActionContext as _, FONT_SIZE_STEP}; use crate::message_bar::{Message, MessageBuffer}; use crate::scheduler::{Scheduler, TimerId, Topic}; @@ -89,6 +96,8 @@ pub enum EventType { Message(Message), Scroll(Scroll), CreateWindow(WindowOptions), + #[cfg(unix)] + IpcConfig(IpcConfig), BlinkCursor, BlinkCursorTimeout, SearchNext, @@ -180,7 +189,7 @@ pub struct ActionContext<'a, N, T> { pub modifiers: &'a mut ModifiersState, pub display: &'a mut Display, pub message_buffer: &'a mut MessageBuffer, - pub config: &'a mut UiConfig, + pub config: &'a UiConfig, pub cursor_blink_timed_out: &'a mut bool, pub event_loop: &'a EventLoopWindowTarget<Event>, pub event_proxy: &'a EventLoopProxy<Event>, @@ -188,6 +197,7 @@ pub struct ActionContext<'a, N, T> { pub search_state: &'a mut SearchState, pub font_size: &'a mut Size, pub dirty: &'a mut bool, + pub occluded: &'a mut bool, pub preserve_title: bool, #[cfg(not(windows))] pub master_fd: RawFd, @@ -471,6 +481,9 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon }; } + // Enable IME so we can input into the search bar with it if we were in Vi mode. + self.window().set_ime_allowed(true); + self.display.pending_update.dirty = true; } @@ -781,7 +794,8 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon /// Toggle the vi mode status. #[inline] fn toggle_vi_mode(&mut self) { - if self.terminal.mode().contains(TermMode::VI) { + let was_in_vi_mode = self.terminal.mode().contains(TermMode::VI); + if was_in_vi_mode { // If we had search running when leaving Vi mode we should mark terminal fully damaged // to cleanup highlighted results. if self.search_state.dfas.take().is_some() { @@ -798,6 +812,9 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon self.cancel_search(); } + // We don't want IME in Vi mode. + self.window().set_ime_allowed(was_in_vi_mode); + self.terminal.toggle_vi_mode(); *self.dirty = true; @@ -931,6 +948,9 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> { /// Cleanup the search state. fn exit_search(&mut self) { + let vi_mode = self.terminal.mode().contains(TermMode::VI); + self.window().set_ime_allowed(!vi_mode); + self.display.pending_update.dirty = true; self.search_state.history_index = None; @@ -950,7 +970,8 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> { // Check terminal cursor style. let terminal_blinking = self.terminal.cursor_style().blinking; let mut blinking = cursor_style.blinking_override().unwrap_or(terminal_blinking); - blinking &= vi_mode || self.terminal().mode().contains(TermMode::SHOW_CURSOR); + blinking &= (vi_mode || self.terminal().mode().contains(TermMode::SHOW_CURSOR)) + && self.display().ime.preedit().is_none(); // Update cursor blinking state. let window_id = self.display.window.id(); @@ -1146,6 +1167,8 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> { TerminalEvent::Exit => (), TerminalEvent::CursorBlinkingChange => self.ctx.update_cursor_blinking(), }, + #[cfg(unix)] + EventType::IpcConfig(_) => (), EventType::ConfigReload(_) | EventType::CreateWindow(_) => (), }, GlutinEvent::RedrawRequested(_) => *self.ctx.dirty = true, @@ -1197,6 +1220,9 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> { self.ctx.update_cursor_blinking(); self.on_focus_change(is_focused); }, + WindowEvent::Occluded(occluded) => { + *self.ctx.occluded = occluded; + }, WindowEvent::DroppedFile(path) => { let path: String = path.to_string_lossy().into(); self.ctx.write_to_pty((path + " ").into_bytes()); @@ -1208,6 +1234,38 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> { *self.ctx.dirty = true; } }, + WindowEvent::Ime(ime) => match ime { + Ime::Commit(text) => { + *self.ctx.dirty = true; + + for ch in text.chars() { + self.received_char(ch); + } + + self.ctx.update_cursor_blinking(); + }, + Ime::Preedit(text, cursor_offset) => { + let preedit = if text.is_empty() { + None + } else { + Some(Preedit::new(text, cursor_offset.map(|offset| offset.0))) + }; + + if self.ctx.display.ime.preedit() != preedit.as_ref() { + self.ctx.display.ime.set_preedit(preedit); + self.ctx.update_cursor_blinking(); + *self.ctx.dirty = true; + } + }, + Ime::Enabled => { + self.ctx.display.ime.set_enabled(true); + *self.ctx.dirty = true; + }, + Ime::Disabled => { + self.ctx.display.ime.set_enabled(false); + *self.ctx.dirty = true; + }, + }, WindowEvent::KeyboardInput { is_synthetic: true, .. } | WindowEvent::TouchpadPressure { .. } | WindowEvent::ScaleFactorChanged { .. } @@ -1241,7 +1299,7 @@ pub struct Processor { wayland_event_queue: Option<EventQueue>, windows: HashMap<WindowId, WindowContext>, cli_options: CliOptions, - config: UiConfig, + config: Rc<UiConfig>, } impl Processor { @@ -1262,8 +1320,8 @@ impl Processor { Processor { windows: HashMap::new(), + config: Rc::new(config), cli_options, - config, #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] wayland_event_queue, } @@ -1277,7 +1335,7 @@ impl Processor { options: WindowOptions, ) -> Result<(), Box<dyn Error>> { let window_context = WindowContext::new( - &self.config, + self.config.clone(), &options, event_loop, proxy, @@ -1289,9 +1347,16 @@ impl Processor { } /// Run the event loop. - pub fn run(&mut self, mut event_loop: EventLoop<Event>) { + /// + /// The result is exit code generate from the loop. + pub fn run( + &mut self, + mut event_loop: EventLoop<Event>, + initial_window_options: WindowOptions, + ) -> Result<(), Box<dyn Error>> { let proxy = event_loop.create_proxy(); let mut scheduler = Scheduler::new(proxy.clone()); + let mut initial_window_options = Some(initial_window_options); // NOTE: Since this takes a pointer to the winit event loop, it MUST be dropped first. #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] @@ -1299,7 +1364,10 @@ impl Processor { #[cfg(any(not(feature = "wayland"), target_os = "macos", windows))] let mut clipboard = Clipboard::new(); - event_loop.run_return(|event, event_loop, control_flow| { + // Disable all device events, since we don't care about them. + event_loop.set_device_event_filter(DeviceEventFilter::Always); + + let exit_code = event_loop.run_return(move |event, event_loop, control_flow| { if self.config.debug.print_events { info!("glutin event: {:?}", event); } @@ -1310,6 +1378,27 @@ impl Processor { } match event { + // The event loop just got initialized. Create a window. + GlutinEvent::Resumed => { + // Creating window inside event loop is required for platforms like macOS to + // properly initialize state, like tab management. Othwerwise the first window + // won't handle tabs. + let initial_window_options = match initial_window_options.take() { + Some(initial_window_options) => initial_window_options, + None => return, + }; + + if let Err(err) = + self.create_window(event_loop, proxy.clone(), initial_window_options) + { + // Log the error right away since we can't return it. + eprintln!("Error: {}", err); + *control_flow = ControlFlow::ExitWithCode(1); + return; + } + + info!("Initialisation complete"); + }, // Check for shutdown. GlutinEvent::UserEvent(Event { window_id: Some(window_id), @@ -1354,7 +1443,6 @@ impl Processor { window_context.handle_event( event_loop, &proxy, - &mut self.config, &mut clipboard, &mut scheduler, GlutinEvent::RedrawEventsCleared, @@ -1375,13 +1463,27 @@ impl Processor { // Load config and update each terminal. if let Ok(config) = config::reload(&path, &self.cli_options) { - let old_config = mem::replace(&mut self.config, config); + self.config = Rc::new(config); for window_context in self.windows.values_mut() { - window_context.update_config(&old_config, &self.config); + window_context.update_config(self.config.clone()); } } }, + // Process IPC config update. + #[cfg(unix)] + GlutinEvent::UserEvent(Event { + payload: EventType::IpcConfig(ipc_config), + window_id, + }) => { + for (_, window_context) in self + .windows + .iter_mut() + .filter(|(id, _)| window_id.is_none() || window_id == Some(**id)) + { + window_context.update_ipc_config(self.config.clone(), ipc_config.clone()); + } + }, // Create a new terminal window. GlutinEvent::UserEvent(Event { payload: EventType::CreateWindow(options), .. @@ -1403,7 +1505,6 @@ impl Processor { window_context.handle_event( event_loop, &proxy, - &mut self.config, &mut clipboard, &mut scheduler, event.clone().into(), @@ -1418,7 +1519,6 @@ impl Processor { window_context.handle_event( event_loop, &proxy, - &mut self.config, &mut clipboard, &mut scheduler, event, @@ -1428,11 +1528,18 @@ impl Processor { _ => (), } }); + + if exit_code == 0 { + Ok(()) + } else { + Err(format!("Event loop terminated with code: {}", exit_code).into()) + } } /// Check if an event is irrelevant and can be skipped. fn skip_event(event: &GlutinEvent<'_, Event>) -> bool { match event { + GlutinEvent::NewEvents(StartCause::Init) => false, GlutinEvent::WindowEvent { event, .. } => matches!( event, WindowEvent::KeyboardInput { is_synthetic: true, .. } diff --git a/alacritty/src/input.rs b/alacritty/src/input.rs index 35aaedda..6dbb72cf 100644 --- a/alacritty/src/input.rs +++ b/alacritty/src/input.rs @@ -369,8 +369,8 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { let display_offset = self.ctx.terminal().grid().display_offset(); let old_point = self.ctx.mouse().point(&size_info, display_offset); - let x = min(max(x, 0), size_info.width() as i32 - 1) as usize; - let y = min(max(y, 0), size_info.height() as i32 - 1) as usize; + let x = x.clamp(0, size_info.width() as i32 - 1) as usize; + let y = y.clamp(0, size_info.height() as i32 - 1) as usize; self.ctx.mouse_mut().x = x; self.ctx.mouse_mut().y = y; @@ -754,6 +754,11 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { /// Process key input. pub fn key_input(&mut self, input: KeyboardInput) { + // IME input will be applied on commit and shouldn't trigger key bindings. + if self.ctx.display().ime.preedit().is_some() { + return; + } + // All key bindings are disabled while a hint is being selected. if self.ctx.display().hint_state.active() { *self.ctx.suppress_chars() = false; @@ -801,6 +806,11 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { pub fn received_char(&mut self, c: char) { let suppress_chars = *self.ctx.suppress_chars(); + // Don't insert chars when we have IME running. + if self.ctx.display().ime.preedit().is_some() { + return; + } + // Handle hint selection over anything else. if self.ctx.display().hint_state.active() && !suppress_chars { self.ctx.hint_input(c); @@ -898,7 +908,7 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { /// Check mouse icon state in relation to the message bar. fn message_bar_cursor_state(&self) -> Option<CursorIcon> { // Since search is above the message bar, the button is offset by search's height. - let search_height = if self.ctx.search_active() { 1 } else { 0 }; + let search_height = usize::from(self.ctx.search_active()); // Calculate Y position of the end of the last terminal line. let size = self.ctx.size_info(); @@ -967,8 +977,7 @@ impl<T: EventListener, A: ActionContext<T>> Processor<T, A> { }; // Scale number of lines scrolled based on distance to boundary. - let delta = delta as i32 / step as i32; - let event = Event::new(EventType::Scroll(Scroll::Delta(delta)), Some(window_id)); + let event = Event::new(EventType::Scroll(Scroll::Delta(delta / step)), Some(window_id)); // Schedule event. let timer_id = TimerId::new(Topic::SelectionScrolling, window_id); diff --git a/alacritty/src/ipc.rs b/alacritty/src/ipc.rs index d4c807ba..368015a9 100644 --- a/alacritty/src/ipc.rs +++ b/alacritty/src/ipc.rs @@ -7,6 +7,7 @@ use std::path::PathBuf; use std::{env, fs, process}; use glutin::event_loop::EventLoopProxy; +use glutin::window::WindowId; use log::warn; use alacritty_terminal::thread; @@ -62,6 +63,14 @@ pub fn spawn_ipc_socket(options: &Options, event_proxy: EventLoopProxy<Event>) - let event = Event::new(EventType::CreateWindow(options), None); let _ = event_proxy.send_event(event); }, + SocketMessage::Config(ipc_config) => { + let window_id = ipc_config + .window_id + .and_then(|id| u64::try_from(id).ok()) + .map(WindowId::from); + let event = Event::new(EventType::IpcConfig(ipc_config), window_id); + let _ = event_proxy.send_event(event); + }, } } }); @@ -110,7 +119,7 @@ fn find_socket(socket_path: Option<PathBuf>) -> IoResult<UnixStream> { // Handle environment variable. if let Ok(path) = env::var(ALACRITTY_SOCKET_ENV) { let socket_path = PathBuf::from(path); - if let Ok(socket) = UnixStream::connect(&socket_path) { + if let Ok(socket) = UnixStream::connect(socket_path) { return Ok(socket); } } diff --git a/alacritty/src/logging.rs b/alacritty/src/logging.rs index f3405e58..113e96ff 100644 --- a/alacritty/src/logging.rs +++ b/alacritty/src/logging.rs @@ -15,15 +15,27 @@ use std::{env, process}; use glutin::event_loop::EventLoopProxy; use log::{self, Level, LevelFilter}; +use alacritty_terminal::config::LOG_TARGET_CONFIG; + use crate::cli::Options; use crate::event::{Event, EventType}; use crate::message_bar::{Message, MessageType}; +/// Logging target for IPC config error messages. +pub const LOG_TARGET_IPC_CONFIG: &str = "alacritty_log_ipc_config"; + /// Name for the environment variable containing the log file's path. const ALACRITTY_LOG_ENV: &str = "ALACRITTY_LOG"; + /// List of targets which will be logged by Alacritty. -const ALLOWED_TARGETS: [&str; 4] = - ["alacritty_terminal", "alacritty_config_derive", "alacritty", "crossfont"]; +const ALLOWED_TARGETS: &[&str] = &[ + LOG_TARGET_IPC_CONFIG, + LOG_TARGET_CONFIG, + "alacritty_config_derive", + "alacritty_terminal", + "alacritty", + "crossfont", +]; pub fn initialize( options: &Options, diff --git a/alacritty/src/main.rs b/alacritty/src/main.rs index a0a98efe..569ed30e 100644 --- a/alacritty/src/main.rs +++ b/alacritty/src/main.rs @@ -14,18 +14,18 @@ compile_error!(r#"at least one of the "x11"/"wayland" features must be enabled"# #[cfg(target_os = "macos")] use std::env; +use std::error::Error; use std::fmt::Write as _; +use std::fs; use std::io::{self, Write}; use std::path::PathBuf; -use std::string::ToString; -use std::{fs, process}; -use glutin::event_loop::EventLoop as GlutinEventLoop; +use glutin::event_loop::EventLoopBuilder as GlutinEventLoopBuilder; #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] use glutin::platform::unix::EventLoopWindowTargetExtUnix; use log::info; #[cfg(windows)] -use winapi::um::wincon::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS}; +use windows_sys::Win32::System::Console::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS}; use alacritty_terminal::tty; @@ -62,7 +62,7 @@ use crate::event::{Event, Processor}; #[cfg(target_os = "macos")] use crate::macos::locale; -fn main() { +fn main() -> Result<(), Box<dyn Error>> { #[cfg(windows)] panic::attach_handler(); @@ -78,25 +78,19 @@ fn main() { let options = Options::new(); #[cfg(unix)] - let result = match options.subcommands { + match options.subcommands { Some(Subcommands::Msg(options)) => msg(options), None => alacritty(options), - }; + } #[cfg(not(unix))] - let result = alacritty(options); - - // Handle command failure. - if let Err(err) = result { - eprintln!("Error: {}", err); - process::exit(1); - } + alacritty(options) } /// `msg` subcommand entrypoint. #[cfg(unix)] -fn msg(options: MessageOptions) -> Result<(), String> { - ipc::send_message(options.socket, options.message).map_err(|err| err.to_string()) +fn msg(options: MessageOptions) -> Result<(), Box<dyn Error>> { + ipc::send_message(options.socket, options.message).map_err(|err| err.into()) } /// Temporary files stored for Alacritty. @@ -129,9 +123,9 @@ impl Drop for TemporaryFiles { /// /// Creates a window, the terminal state, PTY, I/O event loop, input processor, /// config change monitor, and runs the main display loop. -fn alacritty(options: Options) -> Result<(), String> { +fn alacritty(options: Options) -> Result<(), Box<dyn Error>> { // Setup glutin event loop. - let window_event_loop = GlutinEventLoop::<Event>::with_user_event(); + let window_event_loop = GlutinEventLoopBuilder::<Event>::with_user_event().build(); // Initialize the logger as soon as possible as to capture output from other subsystems. let log_file = logging::initialize(&options, window_event_loop.create_proxy()) @@ -191,16 +185,8 @@ fn alacritty(options: Options) -> Result<(), String> { let window_options = options.window_options.clone(); let mut processor = Processor::new(config, options, &window_event_loop); - // Create the first Alacritty window. - let proxy = window_event_loop.create_proxy(); - processor - .create_window(&window_event_loop, proxy, window_options) - .map_err(|err| err.to_string())?; - - info!("Initialisation complete"); - // Start event loop and block until shutdown. - processor.run(window_event_loop); + let result = processor.run(window_event_loop, window_options); // This explicit drop is needed for Windows, ConPTY backend. Otherwise a deadlock can occur. // The cause: @@ -225,7 +211,8 @@ fn alacritty(options: Options) -> Result<(), String> { } info!("Goodbye"); - Ok(()) + + result } fn log_config_path(config: &UiConfig) { diff --git a/alacritty/src/panic.rs b/alacritty/src/panic.rs index 2311d7b9..2637f8d6 100644 --- a/alacritty/src/panic.rs +++ b/alacritty/src/panic.rs @@ -1,7 +1,9 @@ use std::io::Write; -use std::{io, panic, ptr}; +use std::{io, panic}; -use winapi::um::winuser; +use windows_sys::Win32::UI::WindowsAndMessaging::{ + MessageBoxW, MB_ICONERROR, MB_OK, MB_SETFOREGROUND, MB_TASKMODAL, +}; use alacritty_terminal::tty::windows::win32_string; @@ -12,14 +14,11 @@ pub fn attach_handler() { let _ = writeln!(io::stderr(), "{}", panic_info); let msg = format!("{}\n\nPress Ctrl-C to Copy", panic_info); unsafe { - winuser::MessageBoxW( - ptr::null_mut(), + MessageBoxW( + 0isize, win32_string(&msg).as_ptr(), win32_string("Alacritty: Runtime Error").as_ptr(), - winuser::MB_ICONERROR - | winuser::MB_OK - | winuser::MB_SETFOREGROUND - | winuser::MB_TASKMODAL, + MB_ICONERROR | MB_OK | MB_SETFOREGROUND | MB_TASKMODAL, ); } })); diff --git a/alacritty/src/renderer/rects.rs b/alacritty/src/renderer/rects.rs index 113eaab5..3acefa62 100644 --- a/alacritty/src/renderer/rects.rs +++ b/alacritty/src/renderer/rects.rs @@ -337,7 +337,7 @@ impl RectRenderer { continue; } - self.program.set_rect_kind(rect_kind as u8); + self.program.set_rect_kind(rect_kind); // Upload accumulated undercurl vertices. gl::BufferData( diff --git a/alacritty/src/renderer/text/atlas.rs b/alacritty/src/renderer/text/atlas.rs index 0922c570..662d767b 100644 --- a/alacritty/src/renderer/text/atlas.rs +++ b/alacritty/src/renderer/text/atlas.rs @@ -133,8 +133,8 @@ impl Atlas { fn insert_inner(&mut self, glyph: &RasterizedGlyph, active_tex: &mut u32) -> Glyph { let offset_y = self.row_baseline; let offset_x = self.row_extent; - let height = glyph.height as i32; - let width = glyph.width as i32; + let height = glyph.height; + let width = glyph.width; let multicolor; unsafe { @@ -196,9 +196,9 @@ impl Atlas { /// Check if there's room in the current row for given glyph. pub fn room_in_row(&self, raw: &RasterizedGlyph) -> bool { - let next_extent = self.row_extent + raw.width as i32; + let next_extent = self.row_extent + raw.width; let enough_width = next_extent <= self.width; - let enough_height = (raw.height as i32) < (self.height - self.row_baseline); + let enough_height = raw.height < (self.height - self.row_baseline); enough_width && enough_height } diff --git a/alacritty/src/renderer/text/builtin_font.rs b/alacritty/src/renderer/text/builtin_font.rs index a4bf65e3..aeee6d91 100644 --- a/alacritty/src/renderer/text/builtin_font.rs +++ b/alacritty/src/renderer/text/builtin_font.rs @@ -71,14 +71,14 @@ fn box_drawing(character: char, metrics: &Metrics, offset: &Delta<i8>) -> Raster for stroke_size in 0..2 * stroke_size { let stroke_size = stroke_size as f32 / 2.; if character == '\u{2571}' || character == '\u{2573}' { - let h = y_end - stroke_size as f32; + let h = y_end - stroke_size; let from_y = f_x(from_x, h); let to_y = f_x(to_x, h); canvas.draw_line(from_x, from_y, to_x, to_y); } if character == '\u{2572}' || character == '\u{2573}' { - let from_y = g_x(from_x, stroke_size as f32); - let to_y = g_x(to_x, stroke_size as f32); + let from_y = g_x(from_x, stroke_size); + let to_y = g_x(to_x, stroke_size); canvas.draw_line(from_x, from_y, to_x, to_y); } } @@ -90,8 +90,8 @@ fn box_drawing(character: char, metrics: &Metrics, offset: &Delta<i8>) -> Raster left: 0, height: height as i32, width: width as i32, - advance: (0, 0), buffer, + advance: (width as i32, height as i32), }; }, _ => Canvas::new(width, height), @@ -345,7 +345,7 @@ fn box_drawing(character: char, metrics: &Metrics, offset: &Delta<i8>) -> Raster if character == '\u{256d}' || character == '\u{2570}' { let center = canvas.x_center() as usize; - let extra_offset = if stroke_size % 2 == width % 2 { 0 } else { 1 }; + let extra_offset = usize::from(stroke_size % 2 != width % 2); let buffer = canvas.buffer_mut(); for y in 1..height { @@ -363,7 +363,7 @@ fn box_drawing(character: char, metrics: &Metrics, offset: &Delta<i8>) -> Raster if character == '\u{256d}' || character == '\u{256e}' { let center = canvas.y_center() as usize; - let extra_offset = if stroke_size % 2 == height % 2 { 0 } else { 1 }; + let extra_offset = usize::from(stroke_size % 2 != height % 2); let buffer = canvas.buffer_mut(); if extra_offset != 0 { @@ -422,7 +422,7 @@ fn box_drawing(character: char, metrics: &Metrics, offset: &Delta<i8>) -> Raster let x = match character { '\u{2590}' => canvas.x_center(), - '\u{2595}' => width as f32 - rect_width, + '\u{2595}' => width - rect_width, _ => 0., }; @@ -486,8 +486,8 @@ fn box_drawing(character: char, metrics: &Metrics, offset: &Delta<i8>) -> Raster left: 0, height: height as i32, width: width as i32, - advance: (0, 0), buffer, + advance: (width as i32, height as i32), } } @@ -592,13 +592,13 @@ impl Canvas { /// Draws a horizontal straight line from (`x`, `y`) of `size` with the given `stroke_size`. fn draw_h_line(&mut self, x: f32, y: f32, size: f32, stroke_size: usize) { let (start_y, end_y) = self.h_line_bounds(y, stroke_size); - self.draw_rect(x, start_y as f32, size, (end_y - start_y) as f32, COLOR_FILL); + self.draw_rect(x, start_y, size, end_y - start_y, COLOR_FILL); } /// Draws a vertical straight line from (`x`, `y`) of `size` with the given `stroke_size`. fn draw_v_line(&mut self, x: f32, y: f32, size: f32, stroke_size: usize) { let (start_x, end_x) = self.v_line_bounds(x, stroke_size); - self.draw_rect(start_x as f32, y, (end_x - start_x) as f32, size, COLOR_FILL); + self.draw_rect(start_x, y, end_x - start_x, size, COLOR_FILL); } /// Draws a rect from the (`x`, `y`) of the given `width` and `height` using `color`. @@ -750,7 +750,7 @@ impl Canvas { let x_next = (x + 1.).clamp(0., v_line_bounds.1 as f32 - 1.); let x = x.clamp(0., v_line_bounds.1 as f32 - 1.); - let y = y.clamp(0., radius_y as f32); + let y = y.clamp(0., radius_y); self.put_pixel(x, y, color_1); self.put_pixel(x_next, y, color_2); diff --git a/alacritty/src/renderer/text/glsl3.rs b/alacritty/src/renderer/text/glsl3.rs index 23c64653..b35c5ef3 100644 --- a/alacritty/src/renderer/text/glsl3.rs +++ b/alacritty/src/renderer/text/glsl3.rs @@ -50,7 +50,6 @@ impl Glsl3Renderer { unsafe { gl::Enable(gl::BLEND); gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR); - gl::Enable(gl::MULTISAMPLE); // Disable depth mask, as the renderer never uses depth tests. gl::DepthMask(gl::FALSE); diff --git a/alacritty/src/string.rs b/alacritty/src/string.rs index 4a758b34..a111166d 100644 --- a/alacritty/src/string.rs +++ b/alacritty/src/string.rs @@ -5,7 +5,7 @@ use std::str::Chars; use unicode_width::UnicodeWidthChar; /// The action performed by [`StrShortener`]. -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TextAction { /// Yield a spacer. Spacer, @@ -93,7 +93,7 @@ impl<'a> StrShortener<'a> { let num_chars = iter.last().map_or(offset, |(idx, _)| idx + 1); let skip_chars = num_chars - offset; - let text_action = if num_chars <= max_width || shortener.is_none() { + let text_action = if current_len < max_width || shortener.is_none() { TextAction::Char } else { TextAction::Shortener @@ -203,8 +203,8 @@ mod tests { &StrShortener::new(s, len * 2, ShortenDirection::Left, Some('.')).collect::<String>() ); - let s = "こJんにちはP"; - let len = 2 + 1 + 2 + 2 + 2 + 2 + 1; + let s = "ちはP"; + let len = 2 + 2 + 1; assert_eq!( ".", &StrShortener::new(s, 1, ShortenDirection::Right, Some('.')).collect::<String>() @@ -226,7 +226,7 @@ mod tests { ); assert_eq!( - "こ .", + "ち .", &StrShortener::new(s, 3, ShortenDirection::Right, Some('.')).collect::<String>() ); @@ -236,12 +236,12 @@ mod tests { ); assert_eq!( - "こ Jん に ち は P", + "ち は P", &StrShortener::new(s, len * 2, ShortenDirection::Left, Some('.')).collect::<String>() ); assert_eq!( - "こ Jん に ち は P", + "ち は P", &StrShortener::new(s, len * 2, ShortenDirection::Right, Some('.')).collect::<String>() ); } diff --git a/alacritty/src/window_context.rs b/alacritty/src/window_context.rs index 1dd620a4..45adf8b8 100644 --- a/alacritty/src/window_context.rs +++ b/alacritty/src/window_context.rs @@ -6,6 +6,7 @@ use std::io::Write; use std::mem; #[cfg(not(windows))] use std::os::unix::io::{AsRawFd, RawFd}; +use std::rc::Rc; #[cfg(not(any(target_os = "macos", windows)))] use std::sync::atomic::Ordering; use std::sync::Arc; @@ -14,11 +15,12 @@ use crossfont::Size; use glutin::event::{Event as GlutinEvent, ModifiersState, WindowEvent}; use glutin::event_loop::{EventLoopProxy, EventLoopWindowTarget}; use glutin::window::WindowId; -use log::info; +use log::{error, info}; use serde_json as json; #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] use wayland_client::EventQueue; +use alacritty_config::SerdeReplace; use alacritty_terminal::event::Event as TerminalEvent; use alacritty_terminal::event_loop::{EventLoop as PtyEventLoop, Msg, Notifier}; use alacritty_terminal::grid::{Dimensions, Scroll}; @@ -28,12 +30,15 @@ use alacritty_terminal::term::test::TermSize; use alacritty_terminal::term::{Term, TermMode}; use alacritty_terminal::tty; +#[cfg(unix)] +use crate::cli::IpcConfig; use crate::cli::WindowOptions; use crate::clipboard::Clipboard; use crate::config::UiConfig; use crate::display::Display; use crate::event::{ActionContext, Event, EventProxy, EventType, Mouse, SearchState}; use crate::input; +use crate::logging::LOG_TARGET_IPC_CONFIG; use crate::message_bar::MessageBuffer; use crate::scheduler::Scheduler; @@ -52,17 +57,20 @@ pub struct WindowContext { font_size: Size, mouse: Mouse, dirty: bool, + occluded: bool, preserve_title: bool, #[cfg(not(windows))] master_fd: RawFd, #[cfg(not(windows))] shell_pid: u32, + ipc_config: Vec<(String, serde_yaml::Value)>, + config: Rc<UiConfig>, } impl WindowContext { /// Create a new terminal window context. pub fn new( - config: &UiConfig, + config: Rc<UiConfig>, options: &WindowOptions, window_event_loop: &EventLoopWindowTarget<Event>, proxy: EventLoopProxy<Event>, @@ -80,7 +88,7 @@ impl WindowContext { // // The display manages a window and can draw the terminal. let display = Display::new( - config, + &config, window_event_loop, &identity, #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] @@ -108,7 +116,7 @@ impl WindowContext { // The PTY forks a process to run the shell on the slave side of the // pseudoterminal. A file descriptor for the master side is retained for // reading/writing to the shell. - let pty = tty::new(&pty_config, display.size_info.into(), display.window.x11_window_id())?; + let pty = tty::new(&pty_config, display.size_info.into(), display.window.id().into())?; #[cfg(not(windows))] let master_fd = pty.file().as_raw_fd(); @@ -141,57 +149,88 @@ impl WindowContext { event_proxy.send_event(TerminalEvent::CursorBlinkingChange.into()); } + let font_size = config.font.size(); + // Create context for the Alacritty window. Ok(WindowContext { - font_size: config.font.size(), - notifier: Notifier(loop_tx), + preserve_title, + font_size, terminal, display, - preserve_title, #[cfg(not(windows))] master_fd, #[cfg(not(windows))] shell_pid, + config, + notifier: Notifier(loop_tx), cursor_blink_timed_out: Default::default(), suppress_chars: Default::default(), message_buffer: Default::default(), received_count: Default::default(), search_state: Default::default(), event_queue: Default::default(), + ipc_config: Default::default(), modifiers: Default::default(), mouse: Default::default(), dirty: Default::default(), + occluded: Default::default(), }) } /// Update the terminal window to the latest config. - pub fn update_config(&mut self, old_config: &UiConfig, config: &UiConfig) { - self.display.update_config(config); - self.terminal.lock().update_config(&config.terminal_config); + pub fn update_config(&mut self, new_config: Rc<UiConfig>) { + let old_config = mem::replace(&mut self.config, new_config); + + // Apply ipc config if there are overrides. + if !self.ipc_config.is_empty() { + let mut config = (*self.config).clone(); + + // Apply each option, removing broken ones. + let mut i = 0; + while i < self.ipc_config.len() { + let (key, value) = &self.ipc_config[i]; + + match config.replace(key, value.clone()) { + Err(err) => { + error!( + target: LOG_TARGET_IPC_CONFIG, + "Unable to override option '{}': {}", key, err + ); + self.ipc_config.swap_remove(i); + }, + Ok(_) => i += 1, + } + } + + self.config = Rc::new(config); + } + + self.display.update_config(&self.config); + self.terminal.lock().update_config(&self.config.terminal_config); // Reload cursor if its thickness has changed. if (old_config.terminal_config.cursor.thickness() - - config.terminal_config.cursor.thickness()) + - self.config.terminal_config.cursor.thickness()) .abs() > f32::EPSILON { self.display.pending_update.set_cursor_dirty(); } - if old_config.font != config.font { + if old_config.font != self.config.font { // Do not update font size if it has been changed at runtime. if self.font_size == old_config.font.size() { - self.font_size = config.font.size(); + self.font_size = self.config.font.size(); } - let font = config.font.clone().with_size(self.font_size); + let font = self.config.font.clone().with_size(self.font_size); self.display.pending_update.set_font(font); } // Update display if padding options were changed. let window_config = &old_config.window; - if window_config.padding(1.) != config.window.padding(1.) - || window_config.dynamic_padding != config.window.dynamic_padding + if window_config.padding(1.) != self.config.window.padding(1.) + || window_config.dynamic_padding != self.config.window.dynamic_padding { self.display.pending_update.dirty = true; } @@ -204,18 +243,18 @@ impl WindowContext { // │ N │ Y │ N ││ N │ // │ N │ N │ _ ││ Y │ if !self.preserve_title - && (!config.window.dynamic_title + && (!self.config.window.dynamic_title || self.display.window.title() == old_config.window.identity.title) { - self.display.window.set_title(config.window.identity.title.clone()); + self.display.window.set_title(self.config.window.identity.title.clone()); } // Disable shadows for transparent windows on macOS. #[cfg(target_os = "macos")] - self.display.window.set_has_shadow(config.window_opacity() >= 1.0); + self.display.window.set_has_shadow(self.config.window_opacity() >= 1.0); // Update hint keys. - self.display.hint_state.update_alphabet(config.hints.alphabet()); + self.display.hint_state.update_alphabet(self.config.hints.alphabet()); // Update cursor blinking. let event = Event::new(TerminalEvent::CursorBlinkingChange.into(), None); @@ -224,12 +263,48 @@ impl WindowContext { self.dirty = true; } + /// Update the IPC config overrides. + #[cfg(unix)] + pub fn update_ipc_config(&mut self, config: Rc<UiConfig>, ipc_config: IpcConfig) { + // Clear previous IPC errors. + self.message_buffer.remove_target(LOG_TARGET_IPC_CONFIG); + + if ipc_config.reset { + self.ipc_config.clear(); + } else { + for option in &ipc_config.options { + // Separate config key/value. + let (key, value) = match option.split_once('=') { + Some(split) => split, + None => { + error!( + target: LOG_TARGET_IPC_CONFIG, + "'{}': IPC config option missing value", option + ); + continue; + }, + }; + + // Try and parse value as yaml. + match serde_yaml::from_str(value) { + Ok(value) => self.ipc_config.push((key.to_owned(), value)), + Err(err) => error!( + target: LOG_TARGET_IPC_CONFIG, + "'{}': Invalid IPC config value: {:?}", option, err + ), + } + } + } + + // Reload current config to pull new IPC config. + self.update_config(config); + } + /// Process events for this terminal window. pub fn handle_event( &mut self, event_loop: &EventLoopWindowTarget<Event>, event_proxy: &EventLoopProxy<Event>, - config: &mut UiConfig, clipboard: &mut Clipboard, scheduler: &mut Scheduler, event: GlutinEvent<'_, Event>, @@ -276,17 +351,18 @@ impl WindowContext { display: &mut self.display, mouse: &mut self.mouse, dirty: &mut self.dirty, + occluded: &mut self.occluded, terminal: &mut terminal, #[cfg(not(windows))] master_fd: self.master_fd, #[cfg(not(windows))] shell_pid: self.shell_pid, preserve_title: self.preserve_title, + config: &self.config, event_proxy, event_loop, clipboard, scheduler, - config, }; let mut processor = input::Processor::new(context); @@ -303,7 +379,7 @@ impl WindowContext { &self.message_buffer, &self.search_state, old_is_searching, - config, + &self.config, ); self.dirty = true; } @@ -311,7 +387,7 @@ impl WindowContext { if self.dirty || self.mouse.hint_highlight_dirty { self.dirty |= self.display.update_highlighted_hints( &terminal, - config, + &self.config, &self.mouse, self.modifiers, ); @@ -324,7 +400,7 @@ impl WindowContext { return; } - if self.dirty { + if self.dirty && !self.occluded { // Force the display to process any pending display update. self.display.process_renderer_update(); @@ -336,7 +412,7 @@ impl WindowContext { } // Redraw screen. - self.display.draw(terminal, &self.message_buffer, config, &self.search_state); + self.display.draw(terminal, &self.message_buffer, &self.config, &self.search_state); } } diff --git a/alacritty/windows/windows.rc b/alacritty/windows/alacritty.rc index b265d4bc..b265d4bc 100644 --- a/alacritty/windows/windows.rc +++ b/alacritty/windows/alacritty.rc diff --git a/alacritty/windows/wix/alacritty.wxs b/alacritty/windows/wix/alacritty.wxs index 461e97cb..1a002d8f 100644 --- a/alacritty/windows/wix/alacritty.wxs +++ b/alacritty/windows/wix/alacritty.wxs @@ -1,10 +1,10 @@ <?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.11.0-dev" Manufacturer="Alacritty"> + <Product Name="Alacritty" Id="*" UpgradeCode="87c21c74-dbd5-4584-89d5-46d9cd0c40a7" Language="1033" Codepage="1252" Version="0.12.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"/> - <WixVariable Id="WixUILicenseRtf" Value=".\extra\windows\wix\license.rtf"/> + <Icon Id="AlacrittyIco" SourceFile=".\alacritty\windows\alacritty.ico"/> + <WixVariable Id="WixUILicenseRtf" Value=".\alacritty\windows\wix\license.rtf"/> <Property Id="ARPPRODUCTICON" Value="AlacrittyIco"/> <MediaTemplate EmbedCab="yes"/> <UIRef Id="WixUI_Minimal"/> diff --git a/alacritty_config/Cargo.toml b/alacritty_config/Cargo.toml new file mode 100644 index 00000000..dde2d9ac --- /dev/null +++ b/alacritty_config/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "alacritty_config" +version = "0.1.1-dev" +authors = ["Christian Duerr <contact@christianduerr.com>"] +license = "MIT/Apache-2.0" +description = "Alacritty configuration abstractions" +homepage = "https://github.com/alacritty/alacritty" +edition = "2021" +rust-version = "1.57.0" + +[dependencies] +log = { version = "0.4.17", features = ["serde"] } +serde_yaml = "0.8.24" +serde = "1.0.137" diff --git a/alacritty_config/LICENSE-APACHE b/alacritty_config/LICENSE-APACHE new file mode 120000 index 00000000..965b606f --- /dev/null +++ b/alacritty_config/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE
\ No newline at end of file diff --git a/alacritty_config/LICENSE-MIT b/alacritty_config/LICENSE-MIT new file mode 120000 index 00000000..76219eb7 --- /dev/null +++ b/alacritty_config/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT
\ No newline at end of file diff --git a/alacritty_config/src/lib.rs b/alacritty_config/src/lib.rs new file mode 100644 index 00000000..7a467650 --- /dev/null +++ b/alacritty_config/src/lib.rs @@ -0,0 +1,64 @@ +use std::collections::HashMap; +use std::error::Error; + +use log::LevelFilter; +use serde::Deserialize; +use serde_yaml::Value; + +pub trait SerdeReplace { + fn replace(&mut self, key: &str, value: Value) -> Result<(), Box<dyn Error>>; +} + +macro_rules! impl_replace { + ($($ty:ty),*$(,)*) => { + $( + impl SerdeReplace for $ty { + fn replace(&mut self, key: &str, value: Value) -> Result<(), Box<dyn Error>> { + replace_simple(self, key, value) + } + } + )* + }; +} + +#[rustfmt::skip] +impl_replace!( + usize, u8, u16, u32, u64, u128, + isize, i8, i16, i32, i64, i128, + f32, f64, + bool, + char, + String, + LevelFilter, +); + +fn replace_simple<'de, D>(data: &mut D, key: &str, value: Value) -> Result<(), Box<dyn Error>> +where + D: Deserialize<'de>, +{ + if !key.is_empty() { + let error = format!("Fields \"{}\" do not exist", key); + return Err(error.into()); + } + *data = D::deserialize(value)?; + + Ok(()) +} + +impl<'de, T: Deserialize<'de>> SerdeReplace for Vec<T> { + fn replace(&mut self, key: &str, value: Value) -> Result<(), Box<dyn Error>> { + replace_simple(self, key, value) + } +} + +impl<'de, T: Deserialize<'de>> SerdeReplace for Option<T> { + fn replace(&mut self, key: &str, value: Value) -> Result<(), Box<dyn Error>> { + replace_simple(self, key, value) + } +} + +impl<'de, T: Deserialize<'de>> SerdeReplace for HashMap<String, T> { + fn replace(&mut self, key: &str, value: Value) -> Result<(), Box<dyn Error>> { + replace_simple(self, key, value) + } +} diff --git a/alacritty_config_derive/Cargo.toml b/alacritty_config_derive/Cargo.toml index 8584d0d2..f01899b5 100644 --- a/alacritty_config_derive/Cargo.toml +++ b/alacritty_config_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "alacritty_config_derive" -version = "0.1.0" +version = "0.2.1-dev" authors = ["Christian Duerr <contact@christianduerr.com>"] license = "MIT/Apache-2.0" description = "Failure resistant deserialization derive" @@ -16,7 +16,11 @@ syn = { version = "1.0.53", features = ["derive", "parsing", "proc-macro", "prin proc-macro2 = "1.0.24" quote = "1.0.7" +[dev-dependencies.alacritty_config] +path = "../alacritty_config" +version = "0.1.1-dev" + [dev-dependencies] +serde = { version = "1.0.117", features = ["derive"] } serde_yaml = "0.8.14" -serde = "1.0.117" log = "0.4.11" diff --git a/alacritty_config_derive/LICENSE-MIT b/alacritty_config_derive/LICENSE-MIT index 31aa7938..76219eb7 100644..120000 --- a/alacritty_config_derive/LICENSE-MIT +++ b/alacritty_config_derive/LICENSE-MIT @@ -1,23 +1 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +../LICENSE-MIT
\ No newline at end of file diff --git a/alacritty_config_derive/src/de_enum.rs b/alacritty_config_derive/src/config_deserialize/de_enum.rs index 98247c0c..73634e73 100644 --- a/alacritty_config_derive/src/de_enum.rs +++ b/alacritty_config_derive/src/config_deserialize/de_enum.rs @@ -1,9 +1,11 @@ use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::{format_ident, quote}; -use syn::{DataEnum, Ident}; +use syn::{DataEnum, Generics, Ident}; -pub fn derive_deserialize(ident: Ident, data_enum: DataEnum) -> TokenStream { +use crate::serde_replace; + +pub fn derive_deserialize(ident: Ident, generics: Generics, data_enum: DataEnum) -> TokenStream { let visitor = format_ident!("{}Visitor", ident); // Create match arm streams and get a list with all available values. @@ -30,7 +32,7 @@ pub fn derive_deserialize(ident: Ident, data_enum: DataEnum) -> TokenStream { available_values.truncate(available_values.len().saturating_sub(2)); // Generate deserialization impl. - let tokens = quote! { + let mut tokens = quote! { struct #visitor; impl<'de> serde::de::Visitor<'de> for #visitor { type Value = #ident; @@ -62,5 +64,8 @@ pub fn derive_deserialize(ident: Ident, data_enum: DataEnum) -> TokenStream { } }; + // Automatically implement [`alacritty_config::SerdeReplace`]. + tokens.extend(serde_replace::derive_direct(ident, generics)); + tokens.into() } diff --git a/alacritty_config_derive/src/de_struct.rs b/alacritty_config_derive/src/config_deserialize/de_struct.rs index cf7ea141..4245764f 100644 --- a/alacritty_config_derive/src/de_struct.rs +++ b/alacritty_config_derive/src/config_deserialize/de_struct.rs @@ -1,13 +1,12 @@ use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::{format_ident, quote}; -use syn::parse::{self, Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; -use syn::{Error, Field, GenericParam, Generics, Ident, LitStr, Token, Type, TypeParam}; +use syn::{Error, Field, Generics, Ident, Type}; + +use crate::{serde_replace, Attr, GenericsStreams, MULTIPLE_FLATTEN_ERROR}; -/// Error message when attempting to flatten multiple fields. -const MULTIPLE_FLATTEN_ERROR: &str = "At most one instance of #[config(flatten)] is supported"; /// Use this crate's name as log target. const LOG_TARGET: &str = env!("CARGO_PKG_NAME"); @@ -18,20 +17,20 @@ pub fn derive_deserialize<T>( ) -> TokenStream { // Create all necessary tokens for the implementation. let GenericsStreams { unconstrained, constrained, phantoms } = - generics_streams(generics.params); + crate::generics_streams(&generics.params); let FieldStreams { flatten, match_assignments } = fields_deserializer(&fields); let visitor = format_ident!("{}Visitor", ident); // Generate deserialization impl. - let tokens = quote! { + let mut tokens = quote! { #[derive(Default)] #[allow(non_snake_case)] - struct #visitor < #unconstrained > { + struct #visitor <#unconstrained> { #phantoms } - impl<'de, #constrained> serde::de::Visitor<'de> for #visitor < #unconstrained > { - type Value = #ident < #unconstrained >; + impl <'de, #constrained> serde::de::Visitor<'de> for #visitor <#unconstrained> { + type Value = #ident <#unconstrained>; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("a mapping") @@ -61,7 +60,7 @@ pub fn derive_deserialize<T>( } } - impl<'de, #constrained> serde::Deserialize<'de> for #ident < #unconstrained > { + impl <'de, #constrained> serde::Deserialize<'de> for #ident <#unconstrained> { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, @@ -71,6 +70,9 @@ pub fn derive_deserialize<T>( } }; + // Automatically implement [`alacritty_config::SerdeReplace`]. + tokens.extend(serde_replace::derive_recursive(ident, generics, fields)); + tokens.into() } @@ -177,50 +179,3 @@ fn field_deserializer(field_streams: &mut FieldStreams, field: &Field) -> Result Ok(()) } - -/// Field attribute. -struct Attr { - ident: String, - param: Option<LitStr>, -} - -impl Parse for Attr { - fn parse(input: ParseStream<'_>) -> parse::Result<Self> { - let ident = input.parse::<Ident>()?.to_string(); - let param = input.parse::<Token![=]>().and_then(|_| input.parse()).ok(); - Ok(Self { ident, param }) - } -} - -/// Storage for all necessary generics information. -#[derive(Default)] -struct GenericsStreams { - unconstrained: TokenStream2, - constrained: TokenStream2, - phantoms: TokenStream2, -} - -/// Create the necessary generics annotations. -/// -/// This will create three different token streams, which might look like this: -/// - unconstrained: `T` -/// - constrained: `T: Default + Deserialize<'de>` -/// - phantoms: `T: PhantomData<T>,` -fn generics_streams<T>(params: Punctuated<GenericParam, T>) -> GenericsStreams { - let mut generics = GenericsStreams::default(); - - for generic in params { - // NOTE: Lifetimes and const params are not supported. - if let GenericParam::Type(TypeParam { ident, .. }) = generic { - generics.unconstrained.extend(quote!( #ident , )); - generics.constrained.extend(quote! { - #ident : Default + serde::Deserialize<'de> , - }); - generics.phantoms.extend(quote! { - #ident : std::marker::PhantomData < #ident >, - }); - } - } - - generics -} diff --git a/alacritty_config_derive/src/config_deserialize/mod.rs b/alacritty_config_derive/src/config_deserialize/mod.rs new file mode 100644 index 00000000..b1923377 --- /dev/null +++ b/alacritty_config_derive/src/config_deserialize/mod.rs @@ -0,0 +1,22 @@ +use proc_macro::TokenStream; +use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Error, Fields}; + +/// Error if the derive was used on an unsupported type. +const UNSUPPORTED_ERROR: &str = "ConfigDeserialize must be used on an enum or struct with fields"; + +mod de_enum; +mod de_struct; + +pub fn derive(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + match input.data { + Data::Struct(DataStruct { fields: Fields::Named(fields), .. }) => { + de_struct::derive_deserialize(input.ident, input.generics, fields.named) + }, + Data::Enum(data_enum) => { + de_enum::derive_deserialize(input.ident, input.generics, data_enum) + }, + _ => Error::new(input.ident.span(), UNSUPPORTED_ERROR).to_compile_error().into(), + } +} diff --git a/alacritty_config_derive/src/lib.rs b/alacritty_config_derive/src/lib.rs index 8601d5cb..116d4828 100644 --- a/alacritty_config_derive/src/lib.rs +++ b/alacritty_config_derive/src/lib.rs @@ -1,23 +1,28 @@ +#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use)] +#![cfg_attr(feature = "cargo-clippy", deny(warnings))] + use proc_macro::TokenStream; -use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Error, Fields, Path}; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::parse::{self, Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::{GenericParam, Ident, LitStr, Path, Token, TypeParam}; -mod de_enum; -mod de_struct; +mod config_deserialize; +mod serde_replace; -/// Error if the derive was used on an unsupported type. -const UNSUPPORTED_ERROR: &str = "ConfigDeserialize must be used on a struct with fields"; +/// Error message when attempting to flatten multiple fields. +pub(crate) const MULTIPLE_FLATTEN_ERROR: &str = + "At most one instance of #[config(flatten)] is supported"; #[proc_macro_derive(ConfigDeserialize, attributes(config))] pub fn derive_config_deserialize(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - - match input.data { - Data::Struct(DataStruct { fields: Fields::Named(fields), .. }) => { - de_struct::derive_deserialize(input.ident, input.generics, fields.named) - }, - Data::Enum(data_enum) => de_enum::derive_deserialize(input.ident, data_enum), - _ => Error::new(input.ident.span(), UNSUPPORTED_ERROR).to_compile_error().into(), - } + config_deserialize::derive(input) +} + +#[proc_macro_derive(SerdeReplace)] +pub fn derive_serde_replace(input: TokenStream) -> TokenStream { + serde_replace::derive(input) } /// Verify that a token path ends with a specific segment. @@ -25,3 +30,50 @@ pub(crate) fn path_ends_with(path: &Path, segment: &str) -> bool { let segments = path.segments.iter(); segments.last().map_or(false, |s| s.ident == segment) } + +/// Storage for all necessary generics information. +#[derive(Default)] +struct GenericsStreams { + unconstrained: TokenStream2, + constrained: TokenStream2, + phantoms: TokenStream2, +} + +/// Create the necessary generics annotations. +/// +/// This will create three different token streams, which might look like this: +/// - unconstrained: `T` +/// - constrained: `T: Default + Deserialize<'de>` +/// - phantoms: `T: PhantomData<T>,` +pub(crate) fn generics_streams<T>(params: &Punctuated<GenericParam, T>) -> GenericsStreams { + let mut generics = GenericsStreams::default(); + + for generic in params { + // NOTE: Lifetimes and const params are not supported. + if let GenericParam::Type(TypeParam { ident, .. }) = generic { + generics.unconstrained.extend(quote!( #ident , )); + generics.constrained.extend(quote! { + #ident : Default + serde::Deserialize<'de> + alacritty_config::SerdeReplace, + }); + generics.phantoms.extend(quote! { + #ident : std::marker::PhantomData < #ident >, + }); + } + } + + generics +} + +/// Field attribute. +pub(crate) struct Attr { + ident: String, + param: Option<LitStr>, +} + +impl Parse for Attr { + fn parse(input: ParseStream<'_>) -> parse::Result<Self> { + let ident = input.parse::<Ident>()?.to_string(); + let param = input.parse::<Token![=]>().and_then(|_| input.parse()).ok(); + Ok(Self { ident, param }) + } +} diff --git a/alacritty_config_derive/src/serde_replace.rs b/alacritty_config_derive/src/serde_replace.rs new file mode 100644 index 00000000..4a0a6a99 --- /dev/null +++ b/alacritty_config_derive/src/serde_replace.rs @@ -0,0 +1,113 @@ +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::punctuated::Punctuated; +use syn::{ + parse_macro_input, Data, DataStruct, DeriveInput, Error, Field, Fields, Generics, Ident, +}; + +use crate::{Attr, GenericsStreams, MULTIPLE_FLATTEN_ERROR}; + +/// Error if the derive was used on an unsupported type. +const UNSUPPORTED_ERROR: &str = "SerdeReplace must be used on a tuple struct"; + +pub fn derive(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + match input.data { + Data::Struct(DataStruct { fields: Fields::Unnamed(_), .. }) | Data::Enum(_) => { + derive_direct(input.ident, input.generics).into() + }, + Data::Struct(DataStruct { fields: Fields::Named(fields), .. }) => { + derive_recursive(input.ident, input.generics, fields.named).into() + }, + _ => Error::new(input.ident.span(), UNSUPPORTED_ERROR).to_compile_error().into(), + } +} + +pub fn derive_direct(ident: Ident, generics: Generics) -> TokenStream2 { + quote! { + impl <#generics> alacritty_config::SerdeReplace for #ident <#generics> { + fn replace(&mut self, key: &str, value: serde_yaml::Value) -> Result<(), Box<dyn std::error::Error>> { + if !key.is_empty() { + let error = format!("Fields \"{}\" do not exist", key); + return Err(error.into()); + } + *self = serde::Deserialize::deserialize(value)?; + + Ok(()) + } + } + } +} + +pub fn derive_recursive<T>( + ident: Ident, + generics: Generics, + fields: Punctuated<Field, T>, +) -> TokenStream2 { + let GenericsStreams { unconstrained, constrained, .. } = + crate::generics_streams(&generics.params); + let replace_arms = match_arms(&fields); + + quote! { + #[allow(clippy::extra_unused_lifetimes)] + impl <'de, #constrained> alacritty_config::SerdeReplace for #ident <#unconstrained> { + fn replace(&mut self, key: &str, value: serde_yaml::Value) -> Result<(), Box<dyn std::error::Error>> { + if key.is_empty() { + *self = serde::Deserialize::deserialize(value)?; + return Ok(()); + } + + let (field, next_key) = key.split_once('.').unwrap_or((key, "")); + match field { + #replace_arms + _ => { + let error = format!("Field \"{}\" does not exist", field); + return Err(error.into()); + }, + } + + Ok(()) + } + } + } +} + +/// Create SerdeReplace recursive match arms. +fn match_arms<T>(fields: &Punctuated<Field, T>) -> TokenStream2 { + let mut stream = TokenStream2::default(); + let mut flattened_arm = None; + + // Create arm for each field. + for field in fields { + let ident = field.ident.as_ref().expect("unreachable tuple struct"); + let literal = ident.to_string(); + + // Check if #[config(flattened)] attribute is present. + let flatten = field + .attrs + .iter() + .filter_map(|attr| attr.parse_args::<Attr>().ok()) + .any(|parsed| parsed.ident.as_str() == "flatten"); + + if flatten && flattened_arm.is_some() { + return Error::new(ident.span(), MULTIPLE_FLATTEN_ERROR).to_compile_error(); + } else if flatten { + flattened_arm = Some(quote! { + _ => alacritty_config::SerdeReplace::replace(&mut self.#ident, key, value)?, + }); + } else { + stream.extend(quote! { + #literal => alacritty_config::SerdeReplace::replace(&mut self.#ident, next_key, value)?, + }); + } + } + + // Add the flattened catch-all as last match arm. + if let Some(flattened_arm) = flattened_arm.take() { + stream.extend(flattened_arm); + } + + stream +} diff --git a/alacritty_config_derive/tests/config.rs b/alacritty_config_derive/tests/config.rs index 2cdae613..bd449ff8 100644 --- a/alacritty_config_derive/tests/config.rs +++ b/alacritty_config_derive/tests/config.rs @@ -1,8 +1,10 @@ use std::sync::{Arc, Mutex}; use log::{Level, Log, Metadata, Record}; +use serde::Deserialize; -use alacritty_config_derive::ConfigDeserialize; +use alacritty_config::SerdeReplace as _; +use alacritty_config_derive::{ConfigDeserialize, SerdeReplace}; #[derive(ConfigDeserialize, Debug, PartialEq, Eq)] enum TestEnum { @@ -63,6 +65,7 @@ struct Test2<T: Default> { field3: usize, #[config(alias = "aliased")] field4: u8, + newtype: NewType, } #[derive(ConfigDeserialize, Default)] @@ -70,6 +73,9 @@ struct Test3 { flatty: usize, } +#[derive(SerdeReplace, Deserialize, Default, PartialEq, Eq, Debug)] +struct NewType(usize); + #[test] fn config_deserialize() { let logger = unsafe { @@ -105,7 +111,7 @@ fn config_deserialize() { assert_eq!(test.enom_small, TestEnum::One); assert_eq!(test.enom_big, TestEnum::Three); assert_eq!(test.enom_error, Test::default().enom_error); - assert_eq!(test.gone, false); + assert!(!test.gone); assert_eq!(test.nesting.field1, Test::default().nesting.field1); assert_eq!(test.nesting.field2, None); assert_eq!(test.nesting.field3, Test::default().nesting.field3); @@ -159,3 +165,33 @@ impl Log for Logger { fn flush(&self) {} } + +#[test] +fn field_replacement() { + let mut test = Test::default(); + + let value = serde_yaml::to_value(13).unwrap(); + test.replace("nesting.field2", value).unwrap(); + + assert_eq!(test.nesting.field2, Some(13)); +} + +#[test] +fn replace_derive() { + let mut test = Test::default(); + + let value = serde_yaml::to_value(9).unwrap(); + test.replace("nesting.newtype", value).unwrap(); + + assert_eq!(test.nesting.newtype, NewType(9)); +} + +#[test] +fn replace_flatten() { + let mut test = Test::default(); + + let value = serde_yaml::to_value(7).unwrap(); + test.replace("flatty", value).unwrap(); + + assert_eq!(test.flatten.flatty, 7); +} diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index c798e065..bece9713 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "alacritty_terminal" -version = "0.17.0-dev" +version = "0.17.1-dev" authors = ["Christian Duerr <contact@christianduerr.com>", "Joe Wilm <joe@jwilm.com>"] license = "Apache-2.0" description = "Library for writing terminal emulators" @@ -11,12 +11,16 @@ rust-version = "1.57.0" [dependencies.alacritty_config_derive] path = "../alacritty_config_derive" -version = "0.1.0" +version = "0.2.1-dev" + +[dependencies.alacritty_config] +path = "../alacritty_config" +version = "0.1.1-dev" [dependencies] libc = "0.2" bitflags = "1" -parking_lot = "0.11.0" +parking_lot = "0.12.0" serde = { version = "1", features = ["derive", "rc"] } serde_yaml = "0.8" vte = { version = "0.10.0", default-features = false } @@ -30,17 +34,20 @@ dirs = "4.0.0" smallvec = { version = "1.9.0", features = ["serde"] } [target.'cfg(unix)'.dependencies] -nix = "0.22.0" +nix = "0.24.1" signal-hook = "0.3.10" signal-hook-mio = { version = "0.2.1", features = ["support-v0_6"] } [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", "consoleapi", -]} mio-anonymous-pipes = "0.2" +windows-sys = { version = "0.36", features = [ + "Win32_System_Console", + "Win32_Foundation", + "Win32_Security", + "Win32_System_Threading", + "Win32_System_WindowsProgramming", +]} [dev-dependencies] serde_json = "1.0.0" diff --git a/alacritty_terminal/src/config/mod.rs b/alacritty_terminal/src/config/mod.rs index 5822d591..f17c327f 100644 --- a/alacritty_terminal/src/config/mod.rs +++ b/alacritty_terminal/src/config/mod.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use serde::Deserialize; -use alacritty_config_derive::ConfigDeserialize; +use alacritty_config_derive::{ConfigDeserialize, SerdeReplace}; mod scrolling; @@ -12,12 +12,13 @@ use crate::ansi::{CursorShape, CursorStyle}; pub use crate::config::scrolling::{Scrolling, MAX_SCROLLBACK_LINES}; +/// Logging target for config error messages. pub const LOG_TARGET_CONFIG: &str = "alacritty_config_derive"; const MIN_BLINK_INTERVAL: u64 = 10; /// Top-level config type. -#[derive(ConfigDeserialize, Debug, PartialEq, Default)] +#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Default)] pub struct Config { /// TERM env variable. pub env: HashMap<String, String>, @@ -125,7 +126,7 @@ impl Cursor { } } -#[derive(Deserialize, Debug, Copy, Clone, PartialEq, Eq)] +#[derive(SerdeReplace, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] #[serde(untagged)] pub enum ConfigCursorStyle { Shape(CursorShape), @@ -222,7 +223,7 @@ impl Program { } /// Wrapper around f32 that represents a percentage value between 0.0 and 1.0. -#[derive(Deserialize, Clone, Copy, Debug, PartialEq)] +#[derive(SerdeReplace, Deserialize, Clone, Copy, Debug, PartialEq)] pub struct Percentage(f32); impl Default for Percentage { @@ -233,13 +234,7 @@ impl Default for Percentage { impl Percentage { pub fn new(value: f32) -> Self { - Percentage(if value < 0.0 { - 0.0 - } else if value > 1.0 { - 1.0 - } else { - value - }) + Percentage(value.clamp(0., 1.)) } pub fn as_f32(self) -> f32 { diff --git a/alacritty_terminal/src/config/scrolling.rs b/alacritty_terminal/src/config/scrolling.rs index 9a5a718c..f4e55787 100644 --- a/alacritty_terminal/src/config/scrolling.rs +++ b/alacritty_terminal/src/config/scrolling.rs @@ -1,7 +1,7 @@ use serde::de::Error as SerdeError; use serde::{Deserialize, Deserializer}; -use alacritty_config_derive::ConfigDeserialize; +use alacritty_config_derive::{ConfigDeserialize, SerdeReplace}; /// Maximum scrollback amount configurable. pub const MAX_SCROLLBACK_LINES: u32 = 100_000; @@ -31,7 +31,7 @@ impl Scrolling { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(SerdeReplace, Copy, Clone, Debug, PartialEq, Eq)] struct ScrollingHistory(u32); impl Default for ScrollingHistory { diff --git a/alacritty_terminal/src/index.rs b/alacritty_terminal/src/index.rs index e672c752..9464b8d8 100644 --- a/alacritty_terminal/src/index.rs +++ b/alacritty_terminal/src/index.rs @@ -7,6 +7,8 @@ use std::ops::{Add, AddAssign, Deref, Sub, SubAssign}; use serde::{Deserialize, Serialize}; +use alacritty_config_derive::SerdeReplace; + use crate::grid::Dimensions; /// The side of a cell. @@ -222,7 +224,19 @@ impl PartialEq<usize> for Line { /// A column. /// /// Newtype to avoid passing values incorrectly. -#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq, Default, Ord, PartialOrd)] +#[derive( + SerdeReplace, + Serialize, + Deserialize, + Debug, + Copy, + Clone, + Eq, + PartialEq, + Default, + Ord, + PartialOrd, +)] pub struct Column(pub usize); impl fmt::Display for Column { diff --git a/alacritty_terminal/src/term/color.rs b/alacritty_terminal/src/term/color.rs index 1cfdec6b..22b30828 100644 --- a/alacritty_terminal/src/term/color.rs +++ b/alacritty_terminal/src/term/color.rs @@ -7,12 +7,14 @@ use serde::de::{Error as _, Visitor}; use serde::{Deserialize, Deserializer, Serialize}; use serde_yaml::Value; +use alacritty_config_derive::SerdeReplace; + use crate::ansi::NamedColor; /// Number of terminal colors. pub const COUNT: usize = 269; -#[derive(Debug, Eq, PartialEq, Copy, Clone, Default, Serialize)] +#[derive(SerdeReplace, Debug, Eq, PartialEq, Copy, Clone, Default, Serialize)] pub struct Rgb { pub r: u8, pub g: u8, @@ -62,9 +64,9 @@ impl Mul<f32> for Rgb { fn mul(self, rhs: f32) -> Rgb { let result = Rgb { - r: (f32::from(self.r) * rhs).max(0.0).min(255.0) as u8, - g: (f32::from(self.g) * rhs).max(0.0).min(255.0) as u8, - b: (f32::from(self.b) * rhs).max(0.0).min(255.0) as u8, + r: (f32::from(self.r) * rhs).clamp(0.0, 255.0) as u8, + g: (f32::from(self.g) * rhs).clamp(0.0, 255.0) as u8, + b: (f32::from(self.b) * rhs).clamp(0.0, 255.0) as u8, }; trace!("Scaling RGB by {} from {:?} to {:?}", rhs, self, result); @@ -170,7 +172,7 @@ impl FromStr for Rgb { } /// RGB color optionally referencing the cell's foreground or background. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(SerdeReplace, Copy, Clone, Debug, PartialEq, Eq)] pub enum CellRgb { CellForeground, CellBackground, diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index b6848812..d54d9998 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -212,7 +212,7 @@ impl TermDamageState { /// Damage point inside of the viewport. #[inline] fn damage_point(&mut self, point: Point<usize>) { - self.damage_line(point.line, point.column.0 as usize, point.column.0 as usize); + self.damage_line(point.line, point.column.0, point.column.0); } /// Expand `line`'s damage to span at least `left` to `right` column. @@ -238,7 +238,7 @@ impl TermDamageState { }; let start = cmp::max(selection.start.line.0 + display_offset, 0); - let end = cmp::min(cmp::max(selection.end.line.0 + display_offset, 0), last_visible_line); + let end = (selection.end.line.0 + display_offset).clamp(0, last_visible_line); for line in start as usize..=end as usize { self.damage_line(line, 0, num_cols - 1); } @@ -1267,7 +1267,7 @@ impl<T: EventListener> Handler for Term<T> { if self.grid.cursor.point.column > Column(0) { let line = self.grid.cursor.point.line.0 as usize; - let column = self.grid.cursor.point.column.0 as usize; + let column = self.grid.cursor.point.column.0; self.grid.cursor.point.column -= 1; self.grid.cursor.input_needs_wrap = false; self.damage.damage_line(line, column - 1, column); @@ -1570,7 +1570,7 @@ impl<T: EventListener> Handler for Term<T> { self.event_proxy.send_event(Event::ClipboardLoad( clipboard_type, Arc::new(move |text| { - let base64 = base64::encode(&text); + let base64 = base64::encode(text); format!("\x1b]52;{};{}{}", clipboard as char, base64, terminator) }), )); diff --git a/alacritty_terminal/src/tty/unix.rs b/alacritty_terminal/src/tty/unix.rs index f52f0920..63da4f9c 100644 --- a/alacritty_terminal/src/tty/unix.rs +++ b/alacritty_terminal/src/tty/unix.rs @@ -148,7 +148,7 @@ fn default_shell_command(pw: &Passwd<'_>) -> Command { } /// Create a new TTY and return a handle to interact with it. -pub fn new(config: &PtyConfig, window_size: WindowSize, window_id: Option<usize>) -> Result<Pty> { +pub fn new(config: &PtyConfig, window_size: WindowSize, window_id: u64) -> Result<Pty> { let (master, slave) = make_pty(window_size.to_winsize())?; #[cfg(any(target_os = "linux", target_os = "macos"))] @@ -178,13 +178,14 @@ pub fn new(config: &PtyConfig, window_size: WindowSize, window_id: Option<usize> builder.stdout(unsafe { Stdio::from_raw_fd(slave) }); // Setup shell environment. + let window_id = window_id.to_string(); + builder.env("ALACRITTY_WINDOW_ID", &window_id); builder.env("LOGNAME", pw.name); builder.env("USER", pw.name); builder.env("HOME", pw.dir); - if let Some(window_id) = window_id { - builder.env("WINDOWID", format!("{}", window_id)); - } + // Set Window ID for clients relying on X11 hacks. + builder.env("WINDOWID", window_id); unsafe { builder.pre_exec(move || { diff --git a/alacritty_terminal/src/tty/windows/child.rs b/alacritty_terminal/src/tty/windows/child.rs index fc163600..91dd1725 100644 --- a/alacritty_terminal/src/tty/windows/child.rs +++ b/alacritty_terminal/src/tty/windows/child.rs @@ -4,14 +4,16 @@ use std::sync::atomic::{AtomicPtr, Ordering}; use mio_extras::channel::{channel, Receiver, Sender}; -use winapi::shared::ntdef::{BOOLEAN, HANDLE, PVOID}; -use winapi::um::winbase::{RegisterWaitForSingleObject, UnregisterWait, INFINITE}; -use winapi::um::winnt::{WT_EXECUTEINWAITTHREAD, WT_EXECUTEONLYONCE}; +use windows_sys::Win32::Foundation::{BOOLEAN, HANDLE}; +use windows_sys::Win32::System::Threading::{ + RegisterWaitForSingleObject, UnregisterWait, WT_EXECUTEINWAITTHREAD, WT_EXECUTEONLYONCE, +}; +use windows_sys::Win32::System::WindowsProgramming::INFINITE; use crate::tty::ChildEvent; /// WinAPI callback to run when child process exits. -extern "system" fn child_exit_callback(ctx: PVOID, timed_out: BOOLEAN) { +extern "system" fn child_exit_callback(ctx: *mut c_void, timed_out: BOOLEAN) { if timed_out != 0 { return; } @@ -29,7 +31,7 @@ impl ChildExitWatcher { pub fn new(child_handle: HANDLE) -> Result<ChildExitWatcher, Error> { let (event_tx, event_rx) = channel::<ChildEvent>(); - let mut wait_handle: HANDLE = 0 as HANDLE; + let mut wait_handle: HANDLE = 0; let sender_ref = Box::new(event_tx); let success = unsafe { @@ -37,7 +39,7 @@ impl ChildExitWatcher { &mut wait_handle, child_handle, Some(child_exit_callback), - Box::into_raw(sender_ref) as PVOID, + Box::into_raw(sender_ref).cast(), INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE, ) @@ -46,7 +48,10 @@ impl ChildExitWatcher { if success == 0 { Err(Error::last_os_error()) } else { - Ok(ChildExitWatcher { wait_handle: AtomicPtr::from(wait_handle), event_rx }) + Ok(ChildExitWatcher { + wait_handle: AtomicPtr::from(wait_handle as *mut c_void), + event_rx, + }) } } @@ -58,7 +63,7 @@ impl ChildExitWatcher { impl Drop for ChildExitWatcher { fn drop(&mut self) { unsafe { - UnregisterWait(self.wait_handle.load(Ordering::Relaxed)); + UnregisterWait(self.wait_handle.load(Ordering::Relaxed) as HANDLE); } } } @@ -78,7 +83,7 @@ mod tests { const WAIT_TIMEOUT: Duration = Duration::from_millis(200); let mut child = Command::new("cmd.exe").spawn().unwrap(); - let child_exit_watcher = ChildExitWatcher::new(child.as_raw_handle()).unwrap(); + let child_exit_watcher = ChildExitWatcher::new(child.as_raw_handle() as HANDLE).unwrap(); let mut events = Events::with_capacity(1); let poll = Poll::new().unwrap(); diff --git a/alacritty_terminal/src/tty/windows/conpty.rs b/alacritty_terminal/src/tty/windows/conpty.rs index e856a1b1..3f6349de 100644 --- a/alacritty_terminal/src/tty/windows/conpty.rs +++ b/alacritty_terminal/src/tty/windows/conpty.rs @@ -3,17 +3,17 @@ use std::os::windows::io::IntoRawHandle; use std::{mem, ptr}; use mio_anonymous_pipes::{EventedAnonRead, EventedAnonWrite}; -use winapi::shared::basetsd::{PSIZE_T, SIZE_T}; -use winapi::shared::minwindef::BYTE; -use winapi::shared::ntdef::LPWSTR; -use winapi::shared::winerror::S_OK; -use winapi::um::consoleapi::{ClosePseudoConsole, CreatePseudoConsole, ResizePseudoConsole}; -use winapi::um::processthreadsapi::{ + +use windows_sys::core::PWSTR; +use windows_sys::Win32::Foundation::{HANDLE, S_OK}; +use windows_sys::Win32::System::Console::{ + ClosePseudoConsole, CreatePseudoConsole, ResizePseudoConsole, COORD, HPCON, +}; +use windows_sys::Win32::System::Threading::{ CreateProcessW, InitializeProcThreadAttributeList, UpdateProcThreadAttribute, - PROCESS_INFORMATION, STARTUPINFOW, + EXTENDED_STARTUPINFO_PRESENT, PROCESS_INFORMATION, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, + STARTF_USESTDHANDLES, STARTUPINFOEXW, STARTUPINFOW, }; -use winapi::um::winbase::{EXTENDED_STARTUPINFO_PRESENT, STARTF_USESTDHANDLES, STARTUPINFOEXW}; -use winapi::um::wincontypes::{COORD, HPCON}; use crate::config::PtyConfig; use crate::event::{OnResize, WindowSize}; @@ -39,7 +39,7 @@ impl Drop for Conpty { unsafe impl Send for Conpty {} pub fn new(config: &PtyConfig, window_size: WindowSize) -> Option<Pty> { - let mut pty_handle = 0 as HPCON; + let mut pty_handle: HPCON = 0; // Passing 0 as the size parameter allows the "system default" buffer // size to be used. There may be small performance and memory advantages @@ -52,10 +52,10 @@ pub fn new(config: &PtyConfig, window_size: WindowSize) -> Option<Pty> { let result = unsafe { CreatePseudoConsole( window_size.into(), - conin_pty_handle.into_raw_handle(), - conout_pty_handle.into_raw_handle(), + conin_pty_handle.into_raw_handle() as HANDLE, + conout_pty_handle.into_raw_handle() as HANDLE, 0, - &mut pty_handle as *mut HPCON, + &mut pty_handle as *mut _, ) }; @@ -65,11 +65,11 @@ pub fn new(config: &PtyConfig, window_size: WindowSize) -> Option<Pty> { // Prepare child process startup info. - let mut size: SIZE_T = 0; + let mut size: usize = 0; - let mut startup_info_ex: STARTUPINFOEXW = Default::default(); + let mut startup_info_ex: STARTUPINFOEXW = unsafe { mem::zeroed() }; - startup_info_ex.StartupInfo.lpTitle = std::ptr::null_mut() as LPWSTR; + startup_info_ex.StartupInfo.lpTitle = std::ptr::null_mut() as PWSTR; startup_info_ex.StartupInfo.cb = mem::size_of::<STARTUPINFOEXW>() as u32; @@ -80,7 +80,7 @@ pub fn new(config: &PtyConfig, window_size: WindowSize) -> Option<Pty> { // Create the appropriately sized thread attribute list. unsafe { let failure = - InitializeProcThreadAttributeList(ptr::null_mut(), 1, 0, &mut size as PSIZE_T) > 0; + InitializeProcThreadAttributeList(ptr::null_mut(), 1, 0, &mut size as *mut usize) > 0; // This call was expected to return false. if failure { @@ -88,7 +88,7 @@ pub fn new(config: &PtyConfig, window_size: WindowSize) -> Option<Pty> { } } - let mut attr_list: Box<[BYTE]> = vec![0; size].into_boxed_slice(); + let mut attr_list: Box<[u8]> = vec![0; size].into_boxed_slice(); // Set startup info's attribute list & initialize it // @@ -106,7 +106,7 @@ pub fn new(config: &PtyConfig, window_size: WindowSize) -> Option<Pty> { startup_info_ex.lpAttributeList, 1, 0, - &mut size as PSIZE_T, + &mut size as *mut usize, ) > 0; if !success { @@ -119,8 +119,8 @@ pub fn new(config: &PtyConfig, window_size: WindowSize) -> Option<Pty> { success = UpdateProcThreadAttribute( startup_info_ex.lpAttributeList, 0, - 22 | 0x0002_0000, // PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE. - pty_handle, + PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE as usize, + pty_handle as *mut std::ffi::c_void, mem::size_of::<HPCON>(), ptr::null_mut(), ptr::null_mut(), @@ -134,11 +134,11 @@ pub fn new(config: &PtyConfig, window_size: WindowSize) -> Option<Pty> { let cmdline = win32_string(&cmdline(config)); let cwd = config.working_directory.as_ref().map(win32_string); - let mut proc_info: PROCESS_INFORMATION = Default::default(); + let mut proc_info: PROCESS_INFORMATION = unsafe { mem::zeroed() }; unsafe { success = CreateProcessW( ptr::null(), - cmdline.as_ptr() as LPWSTR, + cmdline.as_ptr() as PWSTR, ptr::null_mut(), ptr::null_mut(), false as i32, @@ -158,7 +158,7 @@ pub fn new(config: &PtyConfig, window_size: WindowSize) -> Option<Pty> { let conout = EventedAnonRead::new(conout); let child_watcher = ChildExitWatcher::new(proc_info.hProcess).unwrap(); - let conpty = Conpty { handle: pty_handle }; + let conpty = Conpty { handle: pty_handle as HPCON }; Some(Pty::new(conpty, conout, conin, child_watcher)) } diff --git a/alacritty_terminal/src/tty/windows/mod.rs b/alacritty_terminal/src/tty/windows/mod.rs index aa21ce14..57925f4c 100644 --- a/alacritty_terminal/src/tty/windows/mod.rs +++ b/alacritty_terminal/src/tty/windows/mod.rs @@ -27,7 +27,7 @@ pub struct Pty { child_watcher: ChildExitWatcher, } -pub fn new(config: &PtyConfig, window_size: WindowSize, _window_id: Option<usize>) -> Result<Pty> { +pub fn new(config: &PtyConfig, window_size: WindowSize, _window_id: u64) -> Result<Pty> { conpty::new(config, window_size) .ok_or_else(|| Error::new(ErrorKind::Other, "failed to spawn conpty")) } diff --git a/extra/alacritty-msg.man b/extra/alacritty-msg.man index ed600d40..96c3ee6b 100644 --- a/extra/alacritty-msg.man +++ b/extra/alacritty-msg.man @@ -1,4 +1,4 @@ -.TH ALACRITTY-MSG "1" "October 2021" "alacritty 0.11.0-dev" "User Commands" +.TH ALACRITTY-MSG "1" "October 2021" "alacritty 0.12.0-dev" "User Commands" .SH NAME alacritty-msg \- Send messages to Alacritty .SH "SYNOPSIS" @@ -10,20 +10,43 @@ making it possible to control Alacritty without directly accessing it. \fB\-s\fR, \fB\-\-socket\fR <socket> Path for IPC socket creation .SH "MESSAGES" +.TP \fBcreate-window\fR Create a new window in the same Alacritty process .TP .SH "\tOPTIONS" .RS 12 +.TP \fB\-\-hold\fR Remain open after child process exits - +.TP \fB\-\-working\-directory\fR <working\-directory> Start the shell in the specified working directory - +.TP \fB\-e\fR, \fB\-\-command\fR <command>... Command and args to execute (must be last argument) .RE +.TP +\fBconfig\fR +Update the Alacritty configuration +.TP +.SH "\tARGS" +.RS 12 +.TP +\fB<CONFIG_OPTIONS>...\fR +Configuration file options [example: cursor.style=Beam] +.RE +.TP +.SH "\tOPTIONS" +.RS 12 +.TP +\fB\-w\fR, \fB\-\-window\-id\fR <WINDOW_ID> +Window ID for the new config. + +Use `-1` to apply this change to all windows. + +[default: \fB$ALACRITTY_WINDOW_ID\fR] +.RE .SH "SEE ALSO" See the alacritty github repository at https://github.com/alacritty/alacritty for the full documentation. .SH "BUGS" diff --git a/extra/alacritty.man b/extra/alacritty.man index eec20d60..09503a93 100644 --- a/extra/alacritty.man +++ b/extra/alacritty.man @@ -1,4 +1,4 @@ -.TH ALACRITTY "1" "August 2018" "alacritty 0.11.0-dev" "User Commands" +.TH ALACRITTY "1" "August 2018" "alacritty 0.12.0-dev" "User Commands" .SH NAME Alacritty \- A fast, cross-platform, OpenGL terminal emulator .SH "SYNOPSIS" @@ -32,10 +32,12 @@ Increases the level of verbosity (the max level is \fB\-vvv\fR) Prints version information .SH "OPTIONS" .TP -\fB\-\-class\fR <instance> | <instance>,<general> +\fB\-\-class\fR <general> | <general>,<instance> Defines the window class hint on Linux [default: Alacritty,Alacritty] -On Wayland the instance class sets the `app_id`, while the general class is ignored. +When only the general class is passed, instance will be set to the same value. + +On Wayland the general class sets the `app_id`, while the instance class is ignored. .TP \fB\-e\fR, \fB\-\-command\fR <command>... Command and args to execute (must be last argument) diff --git a/extra/alacritty.yml b/extra/alacritty.yml new file mode 120000 index 00000000..2db9ba40 --- /dev/null +++ b/extra/alacritty.yml @@ -0,0 +1 @@ +../alacritty.yml
\ No newline at end of file diff --git a/extra/completions/_alacritty b/extra/completions/_alacritty index 50a5c00d..6b80a797 100644 --- a/extra/completions/_alacritty +++ b/extra/completions/_alacritty @@ -73,6 +73,17 @@ _arguments "${_arguments_options[@]}" \ '--help[Print help information]' \ && ret=0 ;; +(config) +_arguments "${_arguments_options[@]}" \ +'-w+[Window ID for the new config]:WINDOW_ID: ' \ +'--window-id=[Window ID for the new config]:WINDOW_ID: ' \ +'()-r[Clear all runtime configuration changes]' \ +'()--reset[Clear all runtime configuration changes]' \ +'-h[Print help information]' \ +'--help[Print help information]' \ +'*::options -- Configuration file options \[example\: cursor.style=Beam\]:' \ +&& ret=0 +;; (help) _arguments "${_arguments_options[@]}" \ '*::subcommand -- The subcommand whose help message to display:' \ @@ -100,6 +111,11 @@ _alacritty_commands() { ) _describe -t commands 'alacritty commands' commands "$@" } +(( $+functions[_alacritty__msg__config_commands] )) || +_alacritty__msg__config_commands() { + local commands; commands=() + _describe -t commands 'alacritty msg config commands' commands "$@" +} (( $+functions[_alacritty__msg__create-window_commands] )) || _alacritty__msg__create-window_commands() { local commands; commands=() @@ -119,6 +135,7 @@ _alacritty__msg__help_commands() { _alacritty__msg_commands() { local commands; commands=( 'create-window:Create a new window in the same Alacritty process' \ +'config:Update the Alacritty configuration' \ 'help:Print this message or the help of the given subcommand(s)' \ ) _describe -t commands 'alacritty msg commands' commands "$@" diff --git a/extra/completions/alacritty.bash b/extra/completions/alacritty.bash index 428e9583..5cca6466 100644 --- a/extra/completions/alacritty.bash +++ b/extra/completions/alacritty.bash @@ -12,6 +12,9 @@ _alacritty() { "$1") cmd="alacritty" ;; + config) + cmd+="__config" + ;; create-window) cmd+="__create__window" ;; @@ -100,7 +103,7 @@ _alacritty() { return 0 ;; alacritty__msg) - opts="-s -h --socket --help create-window help" + opts="-s -h --socket --help create-window config help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -121,6 +124,28 @@ _alacritty() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + alacritty__msg__config) + opts="-w -r -h --window-id --reset --help <CONFIG_OPTIONS>..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --window-id) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -w) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; alacritty__msg__create__window) opts="-e -t -h --working-directory --hold --command --title --class --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then diff --git a/extra/completions/alacritty.fish b/extra/completions/alacritty.fish index 69b3d777..fdc24ab2 100644 --- a/extra/completions/alacritty.fish +++ b/extra/completions/alacritty.fish @@ -15,13 +15,17 @@ complete -c alacritty -n "__fish_use_subcommand" -s v -d 'Increases the level of complete -c alacritty -n "__fish_use_subcommand" -l hold -d 'Remain open after child process exit' complete -c alacritty -n "__fish_use_subcommand" -f -a "msg" -d 'Send a message to the Alacritty socket' complete -c alacritty -n "__fish_use_subcommand" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c alacritty -n "__fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from create-window; and not __fish_seen_subcommand_from help" -s s -l socket -d 'IPC socket connection path override' -r -F -complete -c alacritty -n "__fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from create-window; and not __fish_seen_subcommand_from help" -s h -l help -d 'Print help information' -complete -c alacritty -n "__fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from create-window; and not __fish_seen_subcommand_from help" -f -a "create-window" -d 'Create a new window in the same Alacritty process' -complete -c alacritty -n "__fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from create-window; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c alacritty -n "__fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from create-window; and not __fish_seen_subcommand_from config; and not __fish_seen_subcommand_from help" -s s -l socket -d 'IPC socket connection path override' -r -F +complete -c alacritty -n "__fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from create-window; and not __fish_seen_subcommand_from config; and not __fish_seen_subcommand_from help" -s h -l help -d 'Print help information' +complete -c alacritty -n "__fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from create-window; and not __fish_seen_subcommand_from config; and not __fish_seen_subcommand_from help" -f -a "create-window" -d 'Create a new window in the same Alacritty process' +complete -c alacritty -n "__fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from create-window; and not __fish_seen_subcommand_from config; and not __fish_seen_subcommand_from help" -f -a "config" -d 'Update the Alacritty configuration' +complete -c alacritty -n "__fish_seen_subcommand_from msg; and not __fish_seen_subcommand_from create-window; and not __fish_seen_subcommand_from config; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from create-window" -l working-directory -d 'Start the shell in the specified working directory' -r -F complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from create-window" -s e -l command -d 'Command and args to execute (must be last argument)' -r complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from create-window" -s t -l title -d 'Defines the window title [default: Alacritty]' -r complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from create-window" -l class -d 'Defines window class/app_id on X11/Wayland [default: Alacritty]' -r complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from create-window" -l hold -d 'Remain open after child process exit' complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from create-window" -s h -l help -d 'Print help information' +complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from config" -s w -l window-id -d 'Window ID for the new config' -r +complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from config" -s r -l reset -d 'Clear all runtime configuration changes' +complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from config" -s h -l help -d 'Print help information' diff --git a/extra/osx/Alacritty.app/Contents/Info.plist b/extra/osx/Alacritty.app/Contents/Info.plist index 593b9c6b..a3ccf2b7 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.11.0-dev</string> + <string>0.12.0-dev</string> <key>CFBundleSupportedPlatforms</key> <array> <string>MacOSX</string> diff --git a/extra/windows b/extra/windows deleted file mode 120000 index 1c83ac5e..00000000 --- a/extra/windows +++ /dev/null @@ -1 +0,0 @@ -../alacritty/windows
\ No newline at end of file |