diff --git a/Cargo.lock b/Cargo.lock index b1febefc8..f94dd91fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -663,9 +663,9 @@ dependencies = [ [[package]] name = "bit-set" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ddef2995421ab6a5c779542c81ee77c115206f4ad9d5a8e05f4ff49716a3dd" +checksum = "09ec2f926cc3060f09db9ebc5b52823d85268d24bb917e472c0c4bea35780a7d" dependencies = [ "bit-vec 0.9.1", ] @@ -1692,9 +1692,9 @@ dependencies = [ [[package]] name = "dirs" -version = "6.0.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +checksum = "8d57d423b3c82e89b9a24ca3091fee61f456a26edbd28d26c65906f4bc1dcd8f" dependencies = [ "dirs-sys", ] @@ -2111,7 +2111,7 @@ checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "font-kit" version = "0.14.3-1" -source = "git+https://github.com/Havunen/font-kit?rev=cc6f86949e40ef3a5e9b2063f8127cdcb72d7ac4#cc6f86949e40ef3a5e9b2063f8127cdcb72d7ac4" +source = "git+https://github.com/Havunen/font-kit?rev=78fcc88851d7b84e5631c47b99e78d44f20a01a4#78fcc88851d7b84e5631c47b99e78d44f20a01a4" dependencies = [ "bitflags 2.13.2", "byteorder", @@ -3856,30 +3856,10 @@ dependencies = [ "windows", ] -[[package]] -name = "gpu-descriptor" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" -dependencies = [ - "bitflags 2.13.2", - "gpu-descriptor-types", - "hashbrown 0.15.5", -] - -[[package]] -name = "gpu-descriptor-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" -dependencies = [ - "bitflags 2.13.2", -] - [[package]] name = "gpui-ce" version = "0.2.2" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "accesskit", "anyhow", @@ -3955,7 +3935,7 @@ dependencies = [ [[package]] name = "gpui_ce_apple" version = "0.1.0" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "anyhow", "block", @@ -3981,7 +3961,7 @@ dependencies = [ [[package]] name = "gpui_ce_collections" version = "0.2.2" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "gpui_ce_util", "indexmap", @@ -3991,7 +3971,7 @@ dependencies = [ [[package]] name = "gpui_ce_derive_refineable" version = "0.2.2" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "proc-macro2", "quote", @@ -4001,7 +3981,7 @@ dependencies = [ [[package]] name = "gpui_ce_linux" version = "0.1.0" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "accesskit", "accesskit_unix", @@ -4049,7 +4029,7 @@ dependencies = [ [[package]] name = "gpui_ce_macos" version = "0.1.0" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "accesskit", "accesskit_macos", @@ -4098,7 +4078,7 @@ dependencies = [ [[package]] name = "gpui_ce_macros" version = "0.1.0" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "heck", "proc-macro2", @@ -4109,7 +4089,7 @@ dependencies = [ [[package]] name = "gpui_ce_media" version = "0.2.2" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "anyhow", "bindgen", @@ -4123,7 +4103,7 @@ dependencies = [ [[package]] name = "gpui_ce_parley" version = "0.1.0" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "anyhow", "fontique", @@ -4138,7 +4118,7 @@ dependencies = [ [[package]] name = "gpui_ce_platform" version = "0.1.0" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "console_error_panic_hook", "gpui-ce", @@ -4152,7 +4132,7 @@ dependencies = [ [[package]] name = "gpui_ce_refineable" version = "0.2.2" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "gpui_ce_derive_refineable", ] @@ -4160,11 +4140,11 @@ dependencies = [ [[package]] name = "gpui_ce_render" version = "0.1.0" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "gpui-ce", "naga 24.0.0", - "naga 29.0.4", + "naga 30.0.1", "smallvec", "wgsl-rs", "windows-core 0.100.0", @@ -4174,7 +4154,7 @@ dependencies = [ [[package]] name = "gpui_ce_scheduler" version = "0.2.2" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "async-task", "backtrace", @@ -4189,7 +4169,7 @@ dependencies = [ [[package]] name = "gpui_ce_shared_string" version = "0.1.0" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "schemars", "serde", @@ -4199,7 +4179,7 @@ dependencies = [ [[package]] name = "gpui_ce_sum_tree" version = "0.2.2" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "heapless 0.9.3", "log", @@ -4210,7 +4190,7 @@ dependencies = [ [[package]] name = "gpui_ce_util" version = "0.2.6" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "anyhow", "log", @@ -4220,7 +4200,7 @@ dependencies = [ [[package]] name = "gpui_ce_web" version = "0.1.0" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "anyhow", "console_error_panic_hook", @@ -4244,7 +4224,7 @@ dependencies = [ [[package]] name = "gpui_ce_wgpu" version = "0.1.0" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "anyhow", "core-foundation", @@ -4276,7 +4256,7 @@ dependencies = [ [[package]] name = "gpui_ce_windows" version = "0.1.0" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "accesskit", "accesskit_windows", @@ -4309,7 +4289,7 @@ dependencies = [ [[package]] name = "gpui_xim" version = "0.4.0-gpui" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "ahash", "hashbrown 0.17.1", @@ -5455,22 +5435,22 @@ dependencies = [ [[package]] name = "naga" -version = "29.0.4" +version = "30.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2bf919621e7975acb27d881bae2fb993e0d45c8e0446e85e6272971e00dc8df" +checksum = "a616d2fb8c89516ac2723a581f69d6c18576046bed761bd6b305e5618e6ae130" dependencies = [ "arrayvec", - "bit-set 0.9.1", + "bit-set 0.10.0", "bitflags 2.13.2", "cfg-if", "cfg_aliases", "codespan-reporting 0.13.1", "half", - "hashbrown 0.16.1", - "hexf-parse", + "hashbrown 0.17.1", "indexmap", "libm", "log", + "naga-types", "num-traits", "once_cell", "rustc-hash 1.1.0", @@ -5479,6 +5459,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "naga-types" +version = "30.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "590afbf58a6f4f62873cd5cff4468061844bafa1cdf399cc954537c22d768d49" +dependencies = [ + "hashbrown 0.17.1", + "indexmap", + "rustc-hash 1.1.0", + "thiserror 2.0.21", +] + [[package]] name = "ndk-sys" version = "0.6.0+11769913" @@ -5959,6 +5951,7 @@ dependencies = [ "bitflags 2.13.2", "objc2 0.6.5", "objc2-core-foundation", + "objc2-core-graphics", "objc2-foundation 0.3.2", "objc2-metal 0.3.2", ] @@ -9442,9 +9435,9 @@ checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] name = "wgpu" -version = "29.0.4" +version = "30.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e8840e1ba2881d4cbb18d2147627a56af426ff064c0401eb0c8410c6325d07" +checksum = "527ccdf43dd5b2e8676eed9984ce00e2bbb0a1b85b70c1969dcb6cd2eb55ab9e" dependencies = [ "arrayvec", "bitflags 2.13.2", @@ -9452,10 +9445,10 @@ dependencies = [ "cfg-if", "cfg_aliases", "document-features", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "js-sys", "log", - "naga 29.0.4", + "naga 30.0.1", "parking_lot", "portable-atomic", "profiling", @@ -9472,21 +9465,22 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "29.0.4" +version = "30.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f519832254e56965a9940c4af57dcb75f702b6f6fa4a0b172f685395843a4d7" +checksum = "14c018fce9b6270aa203c2fdd56f3cce996713534bd757e4ea58c8560b121f14" dependencies = [ "arrayvec", - "bit-set 0.9.1", + "bit-set 0.10.0", "bit-vec 0.9.1", "bitflags 2.13.2", "bytemuck", "cfg_aliases", "document-features", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "indexmap", "log", - "naga 29.0.4", + "naga 30.0.1", + "naga-types", "once_cell", "parking_lot", "portable-atomic", @@ -9506,50 +9500,50 @@ dependencies = [ [[package]] name = "wgpu-core-deps-apple" -version = "29.0.4" +version = "30.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e39e26c4c0e07589e67d18546cf79ff45383659fc72fca4dd293358a0347f3" +checksum = "061f3d319a40d39d00b1ecc2c33b89fe21d4e6fe01859df3500a3a8ecccd6b68" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-emscripten" -version = "29.0.4" +version = "30.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01e09be551dc939498bdd5f6b2c66e55ab275dad25825267a08605a80fc9f0af" +checksum = "d98b86cf4abf524a902dd35f18ca6a3f08fc2ae9847c8f10b48e30491b1f0b86" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-wasm" -version = "29.0.4" +version = "30.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1fb1798be2a912497d4c224f72d39bb0cb34af50e8bcc29865bc339c943059" +checksum = "d260ca2ca8adc654f513a869bf3cac0b47f5b8d3f87803d6942015f8de597aae" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-windows-linux-android" -version = "29.0.4" +version = "30.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e592c1bbef6ad047647ae6e666ebd8cee7a32bb4544d9700ec96cbf73230257" +checksum = "7586165fd5f6d881cb9ce4bb71f40d6caab2c0f1837e3fc1d9788a197fb6004f" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-hal" -version = "29.0.4" +version = "30.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ace1c17727311c22a46e4e3faf56ea6de81af99dcc839bdfb54857b94d448d" +checksum = "b6b7fb58561a792bc237628ba0792e332de418fefe145f13b5ed8201e6d52f58" dependencies = [ "android_system_properties", "arrayvec", "ash", - "bit-set 0.9.1", + "bit-set 0.10.0", "bitflags 2.13.2", "block2 0.6.2", "bytemuck", @@ -9558,17 +9552,18 @@ dependencies = [ "glow", "glutin_wgl_sys", "gpu-allocator", - "gpu-descriptor", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "js-sys", "khronos-egl", "libc", "libloading", "log", - "naga 29.0.4", + "naga 30.0.1", + "naga-types", "ndk-sys", "objc2 0.6.5", "objc2-core-foundation", + "objc2-core-graphics", "objc2-foundation 0.3.2", "objc2-metal 0.3.2", "objc2-quartz-core 0.3.2", @@ -9583,6 +9578,7 @@ dependencies = [ "raw-window-metal", "renderdoc-sys", "smallvec", + "static_assertions", "thiserror 2.0.21", "wasm-bindgen", "wayland-sys", @@ -9596,36 +9592,38 @@ dependencies = [ [[package]] name = "wgpu-naga-bridge" -version = "29.0.4" +version = "30.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95226013f547544b223281cd16a4fb549aa9dcb562adbda0faae4c73ffbbc161" +checksum = "d2f62e73117bb7a62bfd9c5a5841438a823f6566c6442a808ee269d2d055c081" dependencies = [ - "naga 29.0.4", + "naga 30.0.1", "wgpu-types", ] [[package]] name = "wgpu-types" -version = "29.0.4" +version = "30.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84bf84cd9ca8ca45e2b223a3868f1adf9bfc0c66aeac212e76ee7e40fdadf8f5" +checksum = "99dad6f1fbdbbdb4c278a6508b059d44688f5cebddf78d005a46a31340269286" dependencies = [ "bitflags 2.13.2", "bytemuck", "js-sys", "log", + "naga-types", "raw-window-handle", + "static_assertions", "web-sys", ] [[package]] name = "wgsl-rs" -version = "0.1.0" -source = "git+https://github.com/schell/wgsl-rs?rev=b2de16a1b81e67d76bc265c296184f882864badb#b2de16a1b81e67d76bc265c296184f882864badb" +version = "0.1.0-beta.6" +source = "git+https://github.com/schell/wgsl-rs?rev=41bb92d2c76ea08091da11a6b3b6d539c19e7828#41bb92d2c76ea08091da11a6b3b6d539c19e7828" dependencies = [ "glam", "half", - "naga 29.0.4", + "naga 30.0.1", "paste", "snafu", "wgsl-rs-ir", @@ -9634,13 +9632,13 @@ dependencies = [ [[package]] name = "wgsl-rs-ir" -version = "0.1.0" -source = "git+https://github.com/schell/wgsl-rs?rev=b2de16a1b81e67d76bc265c296184f882864badb#b2de16a1b81e67d76bc265c296184f882864badb" +version = "0.1.0-beta.6" +source = "git+https://github.com/schell/wgsl-rs?rev=41bb92d2c76ea08091da11a6b3b6d539c19e7828#41bb92d2c76ea08091da11a6b3b6d539c19e7828" [[package]] name = "wgsl-rs-macros" -version = "0.1.0" -source = "git+https://github.com/schell/wgsl-rs?rev=b2de16a1b81e67d76bc265c296184f882864badb#b2de16a1b81e67d76bc265c296184f882864badb" +version = "0.1.0-beta.6" +source = "git+https://github.com/schell/wgsl-rs?rev=41bb92d2c76ea08091da11a6b3b6d539c19e7828#41bb92d2c76ea08091da11a6b3b6d539c19e7828" dependencies = [ "itertools 0.14.0", "proc-macro2", @@ -10093,7 +10091,7 @@ checksum = "163b33ed8786455e2fa5d72f554057ce3f3182425434f756cd39c99839d88e23" [[package]] name = "xim-ctext" version = "0.3.0" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "encoding_rs", ] @@ -10101,7 +10099,7 @@ dependencies = [ [[package]] name = "xim-parser" version = "0.2.1" -source = "git+https://github.com/Havunen/gpui-ce.git?rev=0665fb07d59cf7af185e58f44b9f890590abe204#0665fb07d59cf7af185e58f44b9f890590abe204" +source = "git+https://github.com/Havunen/gpui-ce.git?rev=f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca#f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" dependencies = [ "bitflags 2.13.2", ] diff --git a/Cargo.toml b/Cargo.toml index 84fa7055a..224b7c29f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -163,12 +163,12 @@ futures = "0.3.34" gix = { version = "0.89.0", default-features = false, features = ["max-performance-safe", "comfort", "basic", "status", "blame", "sha1", "sha256"] } # Already in the tree via gix; Git-compatible quoting for attribute patterns. gix-quote = "0.10.0" -gpui = { package = "gpui-ce", git = "https://github.com/Havunen/gpui-ce.git", rev = "0665fb07d59cf7af185e58f44b9f890590abe204" } -gpui_platform = { package = "gpui_ce_platform", git = "https://github.com/Havunen/gpui-ce.git", rev = "0665fb07d59cf7af185e58f44b9f890590abe204", features = ["font-kit"] } -gpui_wgpu = { package = "gpui_ce_wgpu", git = "https://github.com/Havunen/gpui-ce.git", rev = "0665fb07d59cf7af185e58f44b9f890590abe204" } -gpui_parley = { package = "gpui_ce_parley", git = "https://github.com/Havunen/gpui-ce.git", rev = "0665fb07d59cf7af185e58f44b9f890590abe204", default-features = false } -gpui_shared_string = { package = "gpui_ce_shared_string", git = "https://github.com/Havunen/gpui-ce.git", rev = "0665fb07d59cf7af185e58f44b9f890590abe204" } -gpui_sum_tree = { package = "gpui_ce_sum_tree", git = "https://github.com/Havunen/gpui-ce.git", rev = "0665fb07d59cf7af185e58f44b9f890590abe204" } +gpui = { package = "gpui-ce", git = "https://github.com/Havunen/gpui-ce.git", rev = "f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" } +gpui_platform = { package = "gpui_ce_platform", git = "https://github.com/Havunen/gpui-ce.git", rev = "f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca", features = ["font-kit"] } +gpui_wgpu = { package = "gpui_ce_wgpu", git = "https://github.com/Havunen/gpui-ce.git", rev = "f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" } +gpui_parley = { package = "gpui_ce_parley", git = "https://github.com/Havunen/gpui-ce.git", rev = "f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca", default-features = false } +gpui_shared_string = { package = "gpui_ce_shared_string", git = "https://github.com/Havunen/gpui-ce.git", rev = "f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" } +gpui_sum_tree = { package = "gpui_ce_sum_tree", git = "https://github.com/Havunen/gpui-ce.git", rev = "f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca" } image = "0.25.10" fast_image_resize = { version = "6.1.0", features = ["image"] } # Already in the dependency graph; merges sorted reference streams lazily. diff --git a/README.md b/README.md index f6441dbc7..e79870518 100644 --- a/README.md +++ b/README.md @@ -254,6 +254,13 @@ To install GnuPG, trust GitHub's signing key, and configure SSH allowed signers, Choose **Settings → Diff → Diff Engine** to make Difftastic the default, or use the diff viewer's cog for a temporary override. Both Split and Inline layouts support it. See [Difftastic](docs/difftastic.md) for partial staging and fallbacks. +### Graphics renderer + +On Linux and Windows, choose **Settings → General → Graphics → Graphics renderer** +to change the renderer for the next launch. See the [Linux](docs/linux-renderer.md) and +[Windows](docs/windows-renderer.md) guides for backend choices and diagnostic +overrides. + ### Themes GitComet supports built-in themes and user-provided custom themes. diff --git a/crates/gitcomet-state/src/session/mod.rs b/crates/gitcomet-state/src/session/mod.rs index b5f94b575..25551bd43 100644 --- a/crates/gitcomet-state/src/session/mod.rs +++ b/crates/gitcomet-state/src/session/mod.rs @@ -42,6 +42,7 @@ pub struct UiSession { pub image_smooth_filter: Option, pub window_controls_mode: Option, pub windows_renderer: Option, + pub linux_renderer: Option, pub browser_open_target: Option, pub ui_density: Option, pub ui_font_size_px: Option, @@ -193,6 +194,8 @@ pub(crate) struct UiSessionFile { #[serde(default, deserialize_with = "lenient")] windows_renderer: Option, #[serde(default, deserialize_with = "lenient")] + linux_renderer: Option, + #[serde(default, deserialize_with = "lenient")] browser_open_target: Option, #[serde(default, deserialize_with = "lenient")] ui_density: Option, @@ -497,6 +500,7 @@ pub fn load_from_path(path: &Path) -> UiSession { image_smooth_filter: file.image_smooth_filter, window_controls_mode: file.window_controls_mode, windows_renderer: file.windows_renderer, + linux_renderer: file.linux_renderer, browser_open_target: file.browser_open_target, ui_density: file.ui_density, ui_font_size_px: file.ui_font_size_px, @@ -785,7 +789,7 @@ fn persist_to_path(path: &Path, session: &impl Serialize) -> io::Result<()> { gitcomet_core::fs_utils::write_private_file(path, &contents) } -fn default_session_file_path() -> Option { +pub fn default_session_file_path() -> Option { #[cfg(test)] if let Some(path) = test_session_file_path_override() { return path; diff --git a/crates/gitcomet-state/src/session/settings.rs b/crates/gitcomet-state/src/session/settings.rs index fc408b050..d42760411 100644 --- a/crates/gitcomet-state/src/session/settings.rs +++ b/crates/gitcomet-state/src/session/settings.rs @@ -15,6 +15,7 @@ pub struct UiSettings { pub image_smooth_filter: Option, pub window_controls_mode: Option, pub windows_renderer: Option, + pub linux_renderer: Option, pub browser_open_target: Option, pub ui_density: Option, pub ui_font_size_px: Option, @@ -121,6 +122,7 @@ pub fn persist_ui_settings_to_path(settings: UiSettings, path: &Path) -> io::Res apply_setting!(settings, file, image_smooth_filter); apply_setting!(settings, file, window_controls_mode); apply_setting!(settings, file, windows_renderer); + apply_setting!(settings, file, linux_renderer); apply_setting!(settings, file, browser_open_target); apply_setting!(settings, file, ui_density); apply_setting!(settings, file, ui_font_size_px); diff --git a/crates/gitcomet-state/src/session/tests.rs b/crates/gitcomet-state/src/session/tests.rs index 809b10241..3a69e15a8 100644 --- a/crates/gitcomet-state/src/session/tests.rs +++ b/crates/gitcomet-state/src/session/tests.rs @@ -3066,6 +3066,61 @@ fn persist_ui_settings_round_trips_window_controls_mode() { assert_eq!(loaded.browser_open_target.as_deref(), Some("new_window")); } +#[test] +fn linux_renderer_roundtrips_without_changing_the_windows_preference() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("session.json"); + assert_eq!(load_from_path(&path).linux_renderer, None); + persist_ui_settings_to_path( + UiSettings { + windows_renderer: Some("dx11".into()), + theme_mode: Some("dark".into()), + ..Default::default() + }, + &path, + ) + .unwrap(); + for value in ["gl", "vulkan", "auto"] { + persist_ui_settings_to_path( + UiSettings { + linux_renderer: Some(value.into()), + ..Default::default() + }, + &path, + ) + .unwrap(); + let loaded = load_from_path(&path); + assert_eq!(loaded.linux_renderer.as_deref(), Some(value)); + assert_eq!(loaded.windows_renderer.as_deref(), Some("dx11")); + assert_eq!(loaded.theme_mode.as_deref(), Some("dark")); + } +} + +#[test] +fn linux_renderer_survives_settings_and_workspace_writes() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("session.json"); + std::fs::write( + &path, + r#"{"version":6,"linux_renderer":"gl","windows_renderer":"dx11","extensions":{"example":{"draft":"keep"}}}"#, + ) + .unwrap(); + persist_ui_settings_to_path( + UiSettings { + theme_mode: Some("dark".into()), + ..Default::default() + }, + &path, + ) + .unwrap(); + persist_workspaces_to_path(&[Workspace::new(vec![dir.path().to_path_buf()])], &path).unwrap(); + let json: serde_json::Value = serde_json::from_slice(&std::fs::read(&path).unwrap()).unwrap(); + assert_eq!(json["linux_renderer"], "gl"); + assert_eq!(json["windows_renderer"], "dx11"); + assert_eq!(json["extensions"]["example"]["draft"], "keep"); + assert_eq!(load_from_path(&path).workspaces.len(), 1); +} + #[test] fn windows_renderer_fallback_survives_other_settings_and_workspace_writes() { let dir = tempfile::tempdir().unwrap(); diff --git a/crates/gitcomet-ui-gpui/assets/open_source_licenses.tsv b/crates/gitcomet-ui-gpui/assets/open_source_licenses.tsv index 3b5c4f240..7c6b32500 100644 --- a/crates/gitcomet-ui-gpui/assets/open_source_licenses.tsv +++ b/crates/gitcomet-ui-gpui/assets/open_source_licenses.tsv @@ -75,9 +75,9 @@ backtrace 0.3.76 MIT OR Apache-2.0 base64 0.22.1 MIT OR Apache-2.0 base64 0.23.1 MIT OR Apache-2.0 bindgen 0.73.2 BSD-3-Clause +bit-set 0.10.0 Apache-2.0 OR MIT bit-set 0.11.1 Apache-2.0 OR MIT bit-set 0.8.0 Apache-2.0 OR MIT -bit-set 0.9.1 Apache-2.0 OR MIT bit-vec 0.10.1 Apache-2.0 OR MIT bit-vec 0.8.0 Apache-2.0 OR MIT bit-vec 0.9.1 Apache-2.0 OR MIT @@ -178,7 +178,7 @@ diff 0.1.13 MIT OR Apache-2.0 difftastic 0.71.0 MIT digest 0.10.7 MIT OR Apache-2.0 digest 0.11.3 MIT OR Apache-2.0 -dirs 6.0.0 MIT OR Apache-2.0 +dirs 7.0.0 MIT OR Apache-2.0 dirs-sys 0.5.0 MIT OR Apache-2.0 dispatch2 0.3.1 Zlib OR Apache-2.0 OR MIT displaydoc 0.2.7 MIT OR Apache-2.0 @@ -312,8 +312,6 @@ glob 0.3.4 MIT OR Apache-2.0 glow 0.17.0 MIT OR Apache-2.0 OR Zlib glutin_wgl_sys 0.6.1 Apache-2.0 gpu-allocator 0.28.0 MIT OR Apache-2.0 -gpu-descriptor 0.3.2 MIT OR Apache-2.0 -gpu-descriptor-types 0.2.0 MIT OR Apache-2.0 gpui-ce 0.2.2 Apache-2.0 gpui_ce_apple 0.1.0 Apache-2.0 gpui_ce_collections 0.2.2 Apache-2.0 @@ -452,7 +450,8 @@ miow 0.6.1 MIT OR Apache-2.0 moxcms 0.8.1 BSD-3-Clause OR Apache-2.0 multiversion_no_op 1.0.0 Apache-2.0 OR MIT naga 24.0.0 MIT OR Apache-2.0 -naga 29.0.4 MIT OR Apache-2.0 +naga 30.0.1 MIT OR Apache-2.0 +naga-types 30.0.1 MIT OR Apache-2.0 ndk-sys 0.6.0+11769913 MIT OR Apache-2.0 new_debug_unreachable 1.0.6 MIT nix 0.31.3 MIT @@ -864,18 +863,18 @@ web-sys 0.3.106 MIT OR Apache-2.0 web-time 1.1.0 MIT OR Apache-2.0 webpki-roots 1.0.10 CDLA-Permissive-2.0 weezl 0.1.12 MIT OR Apache-2.0 -wgpu 29.0.4 MIT OR Apache-2.0 -wgpu-core 29.0.4 MIT OR Apache-2.0 -wgpu-core-deps-apple 29.0.4 MIT OR Apache-2.0 -wgpu-core-deps-emscripten 29.0.4 MIT OR Apache-2.0 -wgpu-core-deps-wasm 29.0.4 MIT OR Apache-2.0 -wgpu-core-deps-windows-linux-android 29.0.4 MIT OR Apache-2.0 -wgpu-hal 29.0.4 MIT OR Apache-2.0 -wgpu-naga-bridge 29.0.4 MIT OR Apache-2.0 -wgpu-types 29.0.4 MIT OR Apache-2.0 -wgsl-rs 0.1.0 MIT -wgsl-rs-ir 0.1.0 MIT -wgsl-rs-macros 0.1.0 MIT +wgpu 30.0.1 MIT OR Apache-2.0 +wgpu-core 30.0.1 MIT OR Apache-2.0 +wgpu-core-deps-apple 30.0.1 MIT OR Apache-2.0 +wgpu-core-deps-emscripten 30.0.1 MIT OR Apache-2.0 +wgpu-core-deps-wasm 30.0.1 MIT OR Apache-2.0 +wgpu-core-deps-windows-linux-android 30.0.1 MIT OR Apache-2.0 +wgpu-hal 30.0.1 MIT OR Apache-2.0 +wgpu-naga-bridge 30.0.1 MIT OR Apache-2.0 +wgpu-types 30.0.1 MIT OR Apache-2.0 +wgsl-rs 0.1.0-beta.6 MIT +wgsl-rs-ir 0.1.0-beta.6 MIT +wgsl-rs-macros 0.1.0-beta.6 MIT which 8.0.6 MIT winapi 0.3.9 MIT/Apache-2.0 winapi-i686-pc-windows-gnu 0.4.0 MIT/Apache-2.0 diff --git a/crates/gitcomet-ui-gpui/src/app/launch.rs b/crates/gitcomet-ui-gpui/src/app/launch.rs index ed7445d7e..656845679 100644 --- a/crates/gitcomet-ui-gpui/src/app/launch.rs +++ b/crates/gitcomet-ui-gpui/src/app/launch.rs @@ -473,7 +473,11 @@ pub(super) fn run_windowed_app( let (application, renderer_session) = crate::windows_renderer::application( launch.view_config.view_mode == GitCometViewMode::Normal, ); - #[cfg(not(target_os = "windows"))] + #[cfg(target_os = "linux")] + let (application, renderer_session) = crate::linux_renderer::application( + launch.view_config.view_mode == GitCometViewMode::Normal, + ); + #[cfg(not(any(target_os = "windows", target_os = "linux")))] let application = gpui_platform::application(); let application = application .with_assets(GitCometAssets::with_extensions(extensions.assets())) @@ -504,7 +508,7 @@ pub(super) fn run_windowed_app( application.run(move |cx: &mut App| { cx.set_global(clean_shutdown_tracker); - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] cx.set_global(renderer_session); crate::ui_probe::start_if_enabled(cx); crate::environment::initialize(cx); @@ -785,13 +789,14 @@ pub(super) fn open_gitcomet_window( ) }) .unwrap_or_else(|err| { - let name = identity::current().display_name(); - panic!( - "failed to open main {name} window: {err}\n\ - This is usually a GPU/display problem, not a {name} bug. \ - If you just updated your system (kernel, mesa, or vulkan drivers), reboot. \ - For diagnostics, include the crash report from the next launch." - ) + #[cfg(target_os = "linux")] + let renderer_help = cx + .try_global::() + .map(|session| session.0.borrow().startup_failure_help()) + .unwrap_or_default(); + #[cfg(not(target_os = "linux"))] + let renderer_help = String::new(); + panic!("{}", window_open_failure_message(&err, &renderer_help)) }); #[cfg(target_os = "macos")] @@ -803,6 +808,17 @@ pub(super) fn open_gitcomet_window( window } +pub(super) fn window_open_failure_message(err: &anyhow::Error, renderer_help: &str) -> String { + let name = identity::current().display_name(); + let mut message = format!("failed to open main {name} window: {err:#}"); + if !renderer_help.is_empty() { + message.push('\n'); + message.push_str(renderer_help); + } + message.push_str("\nFor diagnostics, include the crash report from the next launch."); + message +} + /// Client-side decorations inset a rounded frame into the surface; the pixels /// outside it must show the desktop, not a solid fill, so every platform but /// macOS asks for a transparent surface. diff --git a/crates/gitcomet-ui-gpui/src/app/tests.rs b/crates/gitcomet-ui-gpui/src/app/tests.rs index 101741be5..fd875ed17 100644 --- a/crates/gitcomet-ui-gpui/src/app/tests.rs +++ b/crates/gitcomet-ui-gpui/src/app/tests.rs @@ -12,14 +12,32 @@ use std::time::{Duration, Instant}; struct TestBackend; -#[cfg(target_os = "windows")] +#[test] +fn window_startup_failure_retains_the_gpu_cause_and_renderer_recovery_help() { + let err = anyhow::anyhow!("Gl: No GPU adapter found that can configure the display surface") + .context("failed to initialize a GPU context"); + let message = launch::window_open_failure_message(&err, "renderer recovery instructions"); + assert!(message.contains("failed to initialize a GPU context")); + assert!(message.contains("Gl: No GPU adapter found that can configure the display surface")); + assert!(message.contains("renderer recovery instructions")); + assert!(!message.contains("not a GitComet bug")); +} + +#[cfg(any(target_os = "windows", target_os = "linux"))] #[gpui::test] fn dismissing_a_restart_terminal_guard_retains_the_saved_renderer(cx: &mut gpui::TestAppContext) { + #[cfg(target_os = "linux")] + use crate::linux_renderer::{RendererPreference, RendererSession, RendererState}; + #[cfg(target_os = "windows")] use crate::windows_renderer::{RendererPreference, RendererSession, RendererState}; + #[cfg(target_os = "linux")] + let saved = RendererPreference::Gl; + #[cfg(target_os = "windows")] + let saved = RendererPreference::Dx11; let renderer = RendererSession(std::rc::Rc::new(std::cell::RefCell::new( RendererState::new(RendererPreference::Auto, true), ))); - renderer.0.borrow_mut().saved = RendererPreference::Dx11; + renderer.0.borrow_mut().saved = saved; cx.update(|app| { app.set_global(renderer.clone()); app.set_global(windows::PendingRestart(true)); @@ -47,7 +65,7 @@ fn dismissing_a_restart_terminal_guard_retains_the_saved_renderer(cx: &mut gpui: window_cx.run_until_parked(); window_cx.update(|_, app| { assert!(!app.global::().0); - assert_eq!(renderer.0.borrow().saved, RendererPreference::Dx11); + assert_eq!(renderer.0.borrow().saved, saved); assert!(renderer.0.borrow().restart_required()); assert_eq!(app.windows().len(), 1); }); diff --git a/crates/gitcomet-ui-gpui/src/app/windows.rs b/crates/gitcomet-ui-gpui/src/app/windows.rs index 846f9ab04..11ae96a39 100644 --- a/crates/gitcomet-ui-gpui/src/app/windows.rs +++ b/crates/gitcomet-ui-gpui/src/app/windows.rs @@ -585,10 +585,14 @@ pub(crate) fn close_active_window_or_warn(cx: &mut App) { pub(crate) struct PendingRestart(pub(crate) bool); impl gpui::Global for PendingRestart {} -#[cfg(target_os = "windows")] +#[cfg(any(target_os = "windows", target_os = "linux"))] pub(crate) fn request_restart(cx: &mut App) { + #[cfg(target_os = "linux")] + use crate::linux_renderer::RendererSession; + #[cfg(target_os = "windows")] + use crate::windows_renderer::RendererSession; if cx - .try_global::() + .try_global::() .is_none_or(|session| !session.0.borrow().restart_allowed) { return; @@ -813,4 +817,21 @@ mod restart_tests { assert!(session.0.borrow().restart_required()); }); } + + #[cfg(target_os = "linux")] + #[gpui::test] + fn focused_renderer_changes_cannot_request_an_application_restart( + cx: &mut gpui::TestAppContext, + ) { + use crate::linux_renderer::{RendererPreference, RendererSession, RendererState}; + cx.update(|cx| { + let mut state = RendererState::new(RendererPreference::Auto, false); + state.saved = RendererPreference::Gl; + let session = RendererSession(std::rc::Rc::new(std::cell::RefCell::new(state))); + cx.set_global(session.clone()); + request_restart(cx); + assert!(cx.try_global::().is_none()); + assert_eq!(session.0.borrow().saved, RendererPreference::Gl); + }); + } } diff --git a/crates/gitcomet-ui-gpui/src/lib.rs b/crates/gitcomet-ui-gpui/src/lib.rs index d8d94cd5b..427fd9608 100644 --- a/crates/gitcomet-ui-gpui/src/lib.rs +++ b/crates/gitcomet-ui-gpui/src/lib.rs @@ -15,6 +15,8 @@ mod environment; mod external_editor; pub mod http; mod launch_guard; +#[cfg(target_os = "linux")] +mod linux_renderer; mod menu_labels; #[doc(hidden)] pub mod perf_alloc; diff --git a/crates/gitcomet-ui-gpui/src/linux_renderer.rs b/crates/gitcomet-ui-gpui/src/linux_renderer.rs new file mode 100644 index 000000000..3575e03f0 --- /dev/null +++ b/crates/gitcomet-ui-gpui/src/linux_renderer.rs @@ -0,0 +1,391 @@ +use crate::preference_writer::PreferenceWriter; +use gitcomet_state::session; +use gpui_platform::{ + LinuxRendererBackend, LinuxRendererEvent, LinuxRendererOptions, LinuxRendererPreference, +}; +use std::{cell::RefCell, path::PathBuf, rc::Rc}; + +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub(crate) enum RendererPreference { + #[default] + Auto, + Vulkan, + Gl, +} + +impl RendererPreference { + pub(crate) fn parse(value: Option<&str>) -> Self { + match value { + Some("vulkan") => Self::Vulkan, + Some("gl") => Self::Gl, + _ => Self::Auto, + } + } + + pub(crate) fn key(self) -> &'static str { + match self { + Self::Auto => "auto", + Self::Vulkan => "vulkan", + Self::Gl => "gl", + } + } + + pub(crate) fn label(self) -> &'static str { + match self { + Self::Auto => "Automatic", + Self::Vulkan => "Vulkan", + Self::Gl => "OpenGL", + } + } +} + +pub(crate) fn preference_settings(value: RendererPreference) -> session::UiSettings { + session::UiSettings { + linux_renderer: Some(value.key().into()), + ..Default::default() + } +} + +pub(crate) struct RendererState { + pub writer: PreferenceWriter, + pub saved: RendererPreference, + pub applied: RendererPreference, + pub active: Option, + pub overridden: bool, + pub saving: bool, + pub error: Option, + pub restart_allowed: bool, + startup_session_path: Option, + startup_backend_override: Option, +} + +#[derive(Clone)] +pub(crate) struct RendererSession(pub Rc>); +impl gpui::Global for RendererSession {} + +impl RendererState { + pub(crate) fn new(saved: RendererPreference, restart_allowed: bool) -> Self { + Self { + writer: PreferenceWriter::default(), + saved, + applied: saved, + active: None, + overridden: false, + saving: false, + error: None, + restart_allowed, + startup_session_path: None, + startup_backend_override: None, + } + } + + fn from_startup( + session_path: Option, + backend_override: Option, + restart_allowed: bool, + ) -> Self { + let session_path = session_path.map(|path| std::path::absolute(&path).unwrap_or(path)); + let session = session_path + .as_deref() + .map(session::load_from_path) + .unwrap_or_default(); + let saved = RendererPreference::parse(session.linux_renderer.as_deref()); + let mut state = Self::new(saved, restart_allowed); + state.startup_session_path = session_path; + state.startup_backend_override = backend_override; + state + } + + pub(crate) fn startup_failure_help(&self) -> String { + let mut help = format!( + "Linux graphics renderer preference: {} ({}).", + self.applied.label(), + self.applied.key() + ); + if let Some(value) = &self.startup_backend_override { + help.push_str(&format!( + " WGPU_BACKEND={value:?} overrides the saved preference for this launch. \ + Unset WGPU_BACKEND to use the saved preference." + )); + } + let Some(path) = &self.startup_session_path else { + help.push_str( + " No session settings file was loaded; persistence is disabled or unavailable.", + ); + return help; + }; + if path.is_absolute() { + help.push_str(&format!(" Settings file: {path:?}.")); + } else { + help.push_str(&format!( + " Settings file (relative path; startup working directory could not be resolved): {path:?}." + )); + } + help.push_str( + " Close the app, then set \"linux_renderer\": \"auto\" or remove only that key in this file, \ + preserving the other session data. Then launch the app again." + ); + help + } + + pub(crate) fn restart_required(&self) -> bool { + self.saved != self.applied && !self.overridden + } + + pub(crate) fn status_message(&self) -> String { + let active = match self.active { + Some(LinuxRendererBackend::Vulkan) => "Vulkan", + Some(LinuxRendererBackend::Gl) => "OpenGL", + None => "Starting", + }; + let mut status = format!("Active: {active}."); + if self.overridden { + status.push_str(" Controlled by WGPU_BACKEND."); + } + if self.restart_required() { + status.push_str(" Restart required."); + } + if !self.restart_allowed { + status.push_str(" Changes apply on the next launch."); + } + if let Some(error) = &self.error { + status.push(' '); + status.push_str(error); + } + status + } + + pub(crate) fn complete_manual_save( + &mut self, + sequence: u64, + value: RendererPreference, + result: std::io::Result, + ) -> bool { + if !self.writer.is_current(sequence) { + return false; + } + self.saving = false; + match result { + Ok(true) => { + self.saved = value; + self.error = None; + } + Ok(false) => return false, + Err(error) => { + self.error = Some(format!("Could not save graphics renderer: {error}")); + return false; + } + } + self.restart_required() && self.restart_allowed + } +} + +pub(crate) fn application(restart_allowed: bool) -> (gpui::Application, RendererSession) { + let state = RendererState::from_startup( + session::default_session_file_path(), + std::env::var("WGPU_BACKEND").ok(), + restart_allowed, + ); + let saved = state.applied; + let state = Rc::new(RefCell::new(state)); + let observer = state.clone(); + let options = LinuxRendererOptions { + preference: match saved { + RendererPreference::Auto => LinuxRendererPreference::Auto, + RendererPreference::Vulkan => LinuxRendererPreference::Vulkan, + RendererPreference::Gl => LinuxRendererPreference::Gl, + }, + on_event: Some(Rc::new(move |event| match event { + LinuxRendererEvent::Selected { + backend, + overridden, + } => { + let mut state = observer.borrow_mut(); + state.active = Some(backend); + state.overridden = overridden; + } + })), + }; + ( + gpui_platform::application_with_linux_renderer(options), + RendererSession(state), + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn startup_failure_help_identifies_the_saved_renderer_and_its_exact_session_file() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("custom-session.json"); + for (key, label) in [ + ("auto", "Automatic"), + ("vulkan", "Vulkan"), + ("gl", "OpenGL"), + ] { + let contents = + format!(r#"{{"version":6,"linux_renderer":"{key}","theme_mode":"light"}}"#); + std::fs::write(&path, &contents).unwrap(); + let mut state = RendererState::from_startup(Some(path.clone()), None, true); + state.saved = RendererPreference::Vulkan; + assert_eq!(state.active, None); + assert!(!state.overridden); + let help = state.startup_failure_help(); + assert!(help.contains(&format!( + "Linux graphics renderer preference: {label} ({key})." + ))); + assert!(help.contains(&format!("Settings file: {path:?}"))); + assert!(help.contains(r#"set "linux_renderer": "auto" or remove only that key"#)); + assert!(help.contains("preserving the other session data")); + assert!(!help.contains("WGPU_BACKEND")); + assert_eq!(std::fs::read_to_string(&path).unwrap(), contents); + } + } + + #[test] + fn startup_override_is_reported_even_before_a_backend_was_selected() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("session.json"); + std::fs::write(&path, r#"{"version":6,"linux_renderer":"gl"}"#).unwrap(); + let state = RendererState::from_startup(Some(path), Some("vulkan,gl".into()), false); + assert!(!state.overridden); + assert_eq!(state.active, None); + let help = state.startup_failure_help(); + assert!(help.contains("Linux graphics renderer preference: OpenGL (gl).")); + assert!(help.contains(r#"WGPU_BACKEND="vulkan,gl" overrides the saved preference"#)); + assert!(help.contains("Unset WGPU_BACKEND")); + } + + #[test] + fn startup_relative_settings_path_is_anchored_without_requiring_the_file_to_exist() { + let cwd = std::env::current_dir().unwrap(); + let dir = tempfile::tempdir_in(&cwd).unwrap(); + let relative = dir + .path() + .strip_prefix(&cwd) + .unwrap() + .join("missing-session.json"); + let expected = cwd.join(&relative); + let state = RendererState::from_startup(Some(relative), None, true); + assert!( + state + .startup_failure_help() + .contains(&format!("Settings file: {expected:?}")) + ); + } + + #[test] + fn startup_without_session_persistence_does_not_invent_a_settings_file() { + let state = RendererState::from_startup(None, None, true); + let help = state.startup_failure_help(); + assert!(help.contains("No session settings file was loaded")); + assert!(!help.contains("Settings file:")); + assert!(!help.contains("remove only that key")); + } + + #[test] + fn persisted_preferences_select_each_backend_and_unknown_values_use_automatic() { + for (value, expected) in [ + (None, RendererPreference::Auto), + (Some("auto"), RendererPreference::Auto), + (Some("vulkan"), RendererPreference::Vulkan), + (Some("gl"), RendererPreference::Gl), + (Some("dx11"), RendererPreference::Auto), + (Some("unknown"), RendererPreference::Auto), + ] { + assert_eq!(RendererPreference::parse(value), expected); + } + for (value, key) in [ + (RendererPreference::Auto, "auto"), + (RendererPreference::Vulkan, "vulkan"), + (RendererPreference::Gl, "gl"), + ] { + assert_eq!( + preference_settings(value), + session::UiSettings { + linux_renderer: Some(key.into()), + ..Default::default() + } + ); + } + } + + #[test] + fn save_completes_before_restart_is_offered_and_keeps_the_active_backend() { + let mut state = RendererState::new(RendererPreference::Auto, true); + state.active = Some(LinuxRendererBackend::Vulkan); + let sequence = state.writer.next(); + state.saving = true; + assert!(!state.restart_required()); + assert!(state.complete_manual_save(sequence, RendererPreference::Gl, Ok(true))); + assert!(!state.saving); + assert_eq!(state.saved, RendererPreference::Gl); + assert_eq!(state.applied, RendererPreference::Auto); + assert_eq!(state.active, Some(LinuxRendererBackend::Vulkan)); + assert_eq!(state.status_message(), "Active: Vulkan. Restart required."); + } + + #[test] + fn failed_save_keeps_the_saved_preference_and_does_not_offer_restart() { + let mut state = RendererState::new(RendererPreference::Auto, true); + let sequence = state.writer.next(); + state.saving = true; + assert!(!state.complete_manual_save( + sequence, + RendererPreference::Gl, + Err(std::io::Error::other("read only")), + )); + assert!(!state.saving); + assert_eq!(state.saved, RendererPreference::Auto); + assert!(!state.restart_required()); + assert!( + state + .status_message() + .contains("Could not save graphics renderer: read only") + ); + } + + #[test] + fn stale_completion_cannot_replace_a_newer_preference_or_finish_its_save() { + let mut state = RendererState::new(RendererPreference::Auto, true); + let stale = state.writer.next(); + let current = state.writer.next(); + state.saving = true; + assert!(!state.complete_manual_save(stale, RendererPreference::Gl, Ok(true))); + assert!(state.saving); + assert_eq!(state.saved, RendererPreference::Auto); + assert!(state.complete_manual_save(current, RendererPreference::Vulkan, Ok(true))); + assert_eq!(state.saved, RendererPreference::Vulkan); + } + + #[test] + fn focused_launch_changes_are_saved_for_the_next_launch_without_a_restart_prompt() { + let mut state = RendererState::new(RendererPreference::Gl, false); + let sequence = state.writer.next(); + assert!(!state.complete_manual_save(sequence, RendererPreference::Auto, Ok(true))); + assert_eq!(state.saved, RendererPreference::Auto); + assert!(state.restart_required()); + assert!( + state + .status_message() + .contains("Changes apply on the next launch.") + ); + } + + #[test] + fn environment_override_reports_the_active_backend_without_requiring_restart() { + let mut state = RendererState::new(RendererPreference::Gl, true); + state.active = Some(LinuxRendererBackend::Vulkan); + state.overridden = true; + let sequence = state.writer.next(); + assert!(!state.complete_manual_save(sequence, RendererPreference::Auto, Ok(true))); + assert_eq!(state.saved, RendererPreference::Auto); + assert!(!state.restart_required()); + assert_eq!( + state.status_message(), + "Active: Vulkan. Controlled by WGPU_BACKEND." + ); + } +} diff --git a/crates/gitcomet-ui-gpui/src/view/panels/bottom_status_bar.rs b/crates/gitcomet-ui-gpui/src/view/panels/bottom_status_bar.rs index e9faeaf84..b4747b736 100644 --- a/crates/gitcomet-ui-gpui/src/view/panels/bottom_status_bar.rs +++ b/crates/gitcomet-ui-gpui/src/view/panels/bottom_status_bar.rs @@ -469,7 +469,7 @@ impl BarItemOwner for BottomStatusBarView { .h(scaled_px(12.0)) .flex_shrink_0() .text_color(theme.colors.foreground.secondary) - .group_hover("bottom_status_bar_discord", move |s| { + .group_hover_style("bottom_status_bar_discord", move |s| { s.text_color(theme.colors.accent.foreground) }), ) diff --git a/crates/gitcomet-ui-gpui/src/view/rows/mod.rs b/crates/gitcomet-ui-gpui/src/view/rows/mod.rs index 05107d2ce..24e6014b5 100644 --- a/crates/gitcomet-ui-gpui/src/view/rows/mod.rs +++ b/crates/gitcomet-ui-gpui/src/view/rows/mod.rs @@ -920,10 +920,10 @@ pub(in crate::view) fn file_row_icon_slot( .rounded_full() .bg(disc.resting) .when_some(disc.hover, |badge, (group, hovered)| { - badge.group_hover(group, |badge| badge.bg(hovered)) + badge.group_hover_style(group, |badge| badge.bg(hovered)) }) .when_some(disc.pressed, |badge, (group, pressed)| { - badge.group_active(group, |badge| badge.bg(pressed)) + badge.group_active_style(group, |badge| badge.bg(pressed)) }) .flex() .items_center() diff --git a/crates/gitcomet-ui-gpui/src/view/rows/status.rs b/crates/gitcomet-ui-gpui/src/view/rows/status.rs index f5fc88d3b..8b03a69b3 100644 --- a/crates/gitcomet-ui-gpui/src/view/rows/status.rs +++ b/crates/gitcomet-ui-gpui/src/view/rows/status.rs @@ -750,7 +750,7 @@ fn status_folder_action( .flex() .items_center() .invisible() - .group_hover(row_group, |d| d.visible()) + .group_hover_style(row_group, |d| d.visible()) .child(button) .into_any_element() } @@ -1018,7 +1018,7 @@ fn status_row( .flex() .items_center() .invisible() - .group_hover(row_group.clone(), |d| d.visible()) + .group_hover_style(row_group.clone(), |d| d.visible()) .gap(scaled_px(4.0)) .child(stage_button), ) diff --git a/crates/gitcomet-ui-gpui/src/view/settings_window/cards.rs b/crates/gitcomet-ui-gpui/src/view/settings_window/cards.rs index c68468119..7d6297b64 100644 --- a/crates/gitcomet-ui-gpui/src/view/settings_window/cards.rs +++ b/crates/gitcomet-ui-gpui/src/view/settings_window/cards.rs @@ -41,7 +41,7 @@ impl SettingsWindowView { let card = self.card("settings_window_general", "General", theme); let card = self.general_integration_rows(card, theme, cx); let card = self.general_date_time_rows(card, theme, cx); - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] let card = self.graphics_renderer_rows(card, theme, cx); card } diff --git a/crates/gitcomet-ui-gpui/src/view/settings_window/graphics_renderer.rs b/crates/gitcomet-ui-gpui/src/view/settings_window/graphics_renderer.rs index 3d12de3eb..9dcca65da 100644 --- a/crates/gitcomet-ui-gpui/src/view/settings_window/graphics_renderer.rs +++ b/crates/gitcomet-ui-gpui/src/view/settings_window/graphics_renderer.rs @@ -1,6 +1,10 @@ use super::*; use crate::kit::interaction::{self as controls, ControlInteractionExt as _}; -use crate::windows_renderer::{RendererPreference, RendererSession}; +#[cfg(target_os = "linux")] +use crate::linux_renderer as renderer; +#[cfg(target_os = "windows")] +use crate::windows_renderer as renderer; +use renderer::{RendererPreference, RendererSession}; impl SettingsWindowView { pub(super) fn graphics_renderer_rows( @@ -39,7 +43,8 @@ impl SettingsWindowView { return card; } let mut detail = self.detail_container("settings_graphics_renderer_options", theme); - for (value, id, description) in [ + #[cfg(target_os = "windows")] + let options = [ ( RendererPreference::Auto, "settings_graphics_renderer_auto", @@ -50,7 +55,26 @@ impl SettingsWindowView { "settings_graphics_renderer_dx11", "Use DirectX 11 directly", ), - ] { + ]; + #[cfg(target_os = "linux")] + let options = [ + ( + RendererPreference::Auto, + "settings_graphics_renderer_auto", + "Vulkan when available, otherwise OpenGL", + ), + ( + RendererPreference::Vulkan, + "settings_graphics_renderer_vulkan", + "Use Vulkan directly", + ), + ( + RendererPreference::Gl, + "settings_graphics_renderer_gl", + "Use OpenGL directly", + ), + ]; + for (value, id, description) in options { detail = detail.child( self.option_row( id, @@ -101,7 +125,7 @@ impl SettingsWindowView { let sequence = writer.next(); let write = cx.background_spawn(async move { writer.persist(sequence, || { - session::persist_ui_settings(crate::windows_renderer::preference_settings(value)) + session::persist_ui_settings(renderer::preference_settings(value)) }) }); let window_handle = window.window_handle(); diff --git a/crates/gitcomet-ui-gpui/src/view/settings_window/mod.rs b/crates/gitcomet-ui-gpui/src/view/settings_window/mod.rs index a2d5c9d4b..c3b8b0fb2 100644 --- a/crates/gitcomet-ui-gpui/src/view/settings_window/mod.rs +++ b/crates/gitcomet-ui-gpui/src/view/settings_window/mod.rs @@ -306,7 +306,7 @@ fn remote_url_policy_settings_label(policy: RemoteUrlPolicy) -> String { #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum SettingsSection { - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] GraphicsRenderer, UiScale, WindowControls, @@ -341,7 +341,7 @@ impl SettingsSection { /// mapping keeps the visible page and the expanded row in sync. fn category(self) -> SettingsCategory { match self { - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] Self::GraphicsRenderer => SettingsCategory::General, Self::UiScale | Self::WindowControls | Self::UiFont | Self::EditorFont => { SettingsCategory::Appearance @@ -547,6 +547,10 @@ impl SettingsCategory { { return true; } + #[cfg(target_os = "linux")] + if self == Self::General && "graphics renderer vulkan opengl".contains(&query) { + return true; + } self.search_haystack().contains(query.as_str()) || (matches!(self, Self::Appearance) && theme_names_match(&query)) } @@ -1602,7 +1606,7 @@ mod cards; pub(crate) mod close_guards; mod extension_host; mod extension_pages; -#[cfg(target_os = "windows")] +#[cfg(any(target_os = "windows", target_os = "linux"))] mod graphics_renderer; mod prefs; mod render; diff --git a/crates/gitcomet-ui-gpui/src/view/settings_window/prefs.rs b/crates/gitcomet-ui-gpui/src/view/settings_window/prefs.rs index 055f633ee..b0261282b 100644 --- a/crates/gitcomet-ui-gpui/src/view/settings_window/prefs.rs +++ b/crates/gitcomet-ui-gpui/src/view/settings_window/prefs.rs @@ -28,6 +28,7 @@ impl SettingsWindowView { window_controls_mode: Some(self.window_controls_mode.key().to_string()), // Renderer changes are persisted separately; ordinary preferences must not overwrite a fallback. windows_renderer: None, + linux_renderer: None, browser_open_target: Some(self.browser_open_target.key().to_string()), ui_density: Some(self.appearance_metrics.density.key().to_string()), ui_font_size_px: Some(self.appearance_metrics.ui_font_size_px), diff --git a/crates/gitcomet-ui-gpui/src/view/settings_window/tests.rs b/crates/gitcomet-ui-gpui/src/view/settings_window/tests.rs index 66182de6d..b0ac05f76 100644 --- a/crates/gitcomet-ui-gpui/src/view/settings_window/tests.rs +++ b/crates/gitcomet-ui-gpui/src/view/settings_window/tests.rs @@ -32,6 +32,8 @@ mod appearance; mod chrome_and_layout; mod executables; mod extensions; +#[cfg(target_os = "linux")] +mod graphics_renderer; mod preferences; mod restoration; mod terminal; diff --git a/crates/gitcomet-ui-gpui/src/view/settings_window/tests/graphics_renderer.rs b/crates/gitcomet-ui-gpui/src/view/settings_window/tests/graphics_renderer.rs new file mode 100644 index 000000000..54afbeeff --- /dev/null +++ b/crates/gitcomet-ui-gpui/src/view/settings_window/tests/graphics_renderer.rs @@ -0,0 +1,170 @@ +use super::*; +use crate::linux_renderer::{RendererPreference, RendererSession, RendererState}; + +#[test] +fn linux_renderer_search_finds_the_general_page() { + for query in ["graphics", "graphics renderer", "vulkan", "opengl"] { + assert!(SettingsCategory::General.matches_query(query)); + assert!(!SettingsCategory::Appearance.matches_query(query)); + } + assert_eq!( + SettingsSection::GraphicsRenderer.category(), + SettingsCategory::General + ); +} + +fn isolated_session(test_name: &str, fail_writes: bool) -> Option { + if let Some(path) = std::env::var_os("GITCOMET_RENDERER_TEST_SESSION") { + return Some(path.into()); + } + let dir = tempfile::tempdir().unwrap(); + let path = if fail_writes { + dir.path().to_path_buf() + } else { + let path = dir.path().join("session.json"); + session::persist_ui_settings_to_path( + session::UiSettings { + linux_renderer: Some("auto".into()), + windows_renderer: Some("dx11".into()), + ..Default::default() + }, + &path, + ) + .unwrap(); + path + }; + let output = Command::new(std::env::current_exe().unwrap()) + .arg(format!( + "view::settings_window::tests::graphics_renderer::{test_name}" + )) + .arg("--exact") + .arg("--nocapture") + .env("GITCOMET_RENDERER_TEST_SESSION", &path) + .env("GITCOMET_SESSION_FILE", &path) + .output() + .unwrap(); + assert!( + output.status.success() + && String::from_utf8_lossy(&output.stdout) + .contains("test result: ok. 1 passed; 0 failed; 0 ignored;"), + "renderer subtest failed:\n{}\n{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + None +} + +#[gpui::test] +fn linux_choices_save_before_restart_and_respect_overrides_and_focused_launches( + cx: &mut gpui::TestAppContext, +) { + let Some(path) = isolated_session( + "linux_choices_save_before_restart_and_respect_overrides_and_focused_launches", + false, + ) else { + return; + }; + let session = RendererSession(std::rc::Rc::new(std::cell::RefCell::new( + RendererState::new(RendererPreference::Auto, true), + ))); + cx.update(|app| app.set_global(session.clone())); + let (settings, cx) = cx.add_window_view(SettingsWindowView::new); + cx.simulate_resize(size(px(720.0), px(1600.0))); + settings.update(cx, |settings, cx| { + settings.select_category(SettingsCategory::General, cx); + settings.set_expanded_section(Some(SettingsSection::GraphicsRenderer), cx); + assert_eq!(settings.preference_settings().linux_renderer, None); + assert_eq!(settings.preference_settings().windows_renderer, None); + }); + crate::view::test_support::redraw(cx); + let automatic = cx.debug_bounds("settings_graphics_renderer_auto").unwrap(); + let vulkan = cx + .debug_bounds("settings_graphics_renderer_vulkan") + .unwrap(); + let gl = cx.debug_bounds("settings_graphics_renderer_gl").unwrap(); + assert!(automatic.bottom() <= vulkan.top()); + assert!(vulkan.bottom() <= gl.top()); + cx.simulate_click(gl.center(), Modifiers::default()); + cx.run_until_parked(); + assert!(cx.has_pending_prompt()); + assert_eq!(session.0.borrow().saved, RendererPreference::Gl); + let saved = session::load_from_path(&path); + assert_eq!(saved.linux_renderer.as_deref(), Some("gl")); + assert_eq!(saved.windows_renderer.as_deref(), Some("dx11")); + cx.simulate_prompt_answer("Later"); + cx.run_until_parked(); + cx.update(|_, app| { + assert!(app.try_global::().is_none()); + }); + + session.0.borrow_mut().overridden = true; + crate::view::test_support::redraw(cx); + let vulkan = cx + .debug_bounds("settings_graphics_renderer_vulkan") + .unwrap(); + cx.simulate_click(vulkan.center(), Modifiers::default()); + cx.run_until_parked(); + assert!(!cx.has_pending_prompt()); + assert!(!session.0.borrow().saving); + assert_eq!(session.0.borrow().saved, RendererPreference::Gl); + assert_eq!( + session::load_from_path(&path).linux_renderer.as_deref(), + Some("gl") + ); + + { + let mut state = session.0.borrow_mut(); + state.overridden = false; + state.restart_allowed = false; + } + crate::view::test_support::redraw(cx); + let vulkan = cx + .debug_bounds("settings_graphics_renderer_vulkan") + .unwrap(); + cx.simulate_click(vulkan.center(), Modifiers::default()); + cx.run_until_parked(); + assert!(!cx.has_pending_prompt()); + assert_eq!(session.0.borrow().saved, RendererPreference::Vulkan); + assert_eq!( + session::load_from_path(&path).linux_renderer.as_deref(), + Some("vulkan") + ); +} + +#[gpui::test] +fn linux_renderer_save_failure_keeps_the_previous_selection_without_a_restart_prompt( + cx: &mut gpui::TestAppContext, +) { + let Some(path) = isolated_session( + "linux_renderer_save_failure_keeps_the_previous_selection_without_a_restart_prompt", + true, + ) else { + return; + }; + let session = RendererSession(std::rc::Rc::new(std::cell::RefCell::new( + RendererState::new(RendererPreference::Auto, true), + ))); + cx.update(|app| app.set_global(session.clone())); + let (settings, cx) = cx.add_window_view(SettingsWindowView::new); + cx.simulate_resize(size(px(720.0), px(1600.0))); + settings.update(cx, |settings, cx| { + settings.select_category(SettingsCategory::General, cx); + settings.set_expanded_section(Some(SettingsSection::GraphicsRenderer), cx); + }); + crate::view::test_support::redraw(cx); + let gl = cx.debug_bounds("settings_graphics_renderer_gl").unwrap(); + cx.simulate_click(gl.center(), Modifiers::default()); + cx.run_until_parked(); + assert!(!cx.has_pending_prompt()); + let state = session.0.borrow(); + assert!(!state.saving); + assert_eq!(state.saved, RendererPreference::Auto); + assert!(!state.restart_required()); + assert!( + state + .status_message() + .contains("Could not save graphics renderer:") + ); + assert!(path.is_dir()); + assert_eq!(std::fs::read_dir(path).unwrap().count(), 0); +} diff --git a/crates/gitcomet-ui-gpui/src/view/ui_persistence.rs b/crates/gitcomet-ui-gpui/src/view/ui_persistence.rs index 7155eea37..e43e53b22 100644 --- a/crates/gitcomet-ui-gpui/src/view/ui_persistence.rs +++ b/crates/gitcomet-ui-gpui/src/view/ui_persistence.rs @@ -191,6 +191,7 @@ impl GitCometView { image_smooth_filter: None, // Owned by the settings window; None preserves it. windows_renderer: None, + linux_renderer: None, window_controls_mode: None, browser_open_target: None, ui_density: Some(crate::appearance::current(cx).density.key().to_string()), diff --git a/crates/gitcomet-ui-kit/gpui-revision b/crates/gitcomet-ui-kit/gpui-revision index b6b1ca4fa..c6f6b638c 100644 --- a/crates/gitcomet-ui-kit/gpui-revision +++ b/crates/gitcomet-ui-kit/gpui-revision @@ -1 +1 @@ -0665fb07d59cf7af185e58f44b9f890590abe204 +f76fe5dc0387cdbeab6db3b05e6cf73ab0caf6ca diff --git a/crates/gitcomet-ui-kit/src/components/picker_prompt.rs b/crates/gitcomet-ui-kit/src/components/picker_prompt.rs index be4eea9b4..89ccc9480 100644 --- a/crates/gitcomet-ui-kit/src/components/picker_prompt.rs +++ b/crates/gitcomet-ui-kit/src/components/picker_prompt.rs @@ -1842,7 +1842,7 @@ pub fn remove_row_button( .when(!always_visible, |button| { button .invisible() - .group_hover(row_group, |style| style.visible()) + .group_hover_style(row_group, |style| style.visible()) }) .control_interaction( InteractionStyle::destructive(theme), diff --git a/crates/gitcomet-ui-kit/src/components/text_fade.rs b/crates/gitcomet-ui-kit/src/components/text_fade.rs index 6ebd95a21..f21e2da5c 100644 --- a/crates/gitcomet-ui-kit/src/components/text_fade.rs +++ b/crates/gitcomet-ui-kit/src/components/text_fade.rs @@ -61,7 +61,7 @@ impl FadingText { .w(ui_scale.px(FADE_WIDTH_PX)) .bg(fade_gradient(self.bg)); if let Some((group, hover_bg)) = self.hover { - fade = fade.group_hover(group, move |s| s.bg(fade_gradient(hover_bg))); + fade = fade.group_hover_style(group, move |s| s.bg(fade_gradient(hover_bg))); } div() diff --git a/crates/gitcomet-ui-kit/src/interaction.rs b/crates/gitcomet-ui-kit/src/interaction.rs index 27aa67c3c..94bd4ae58 100644 --- a/crates/gitcomet-ui-kit/src/interaction.rs +++ b/crates/gitcomet-ui-kit/src/interaction.rs @@ -301,7 +301,7 @@ impl InteractionStyle { } let outline = interaction_outline(self.theme.colors.interaction.focus_ring); - control = control.focus_visible(move |style| style.shadow(vec![outline])); + control = control.focus_visible_style(move |style| style.shadow(vec![outline])); if !self.pointer_feedback { return control.cursor(CursorStyle::Arrow); } @@ -311,12 +311,12 @@ impl InteractionStyle { control .cursor(CursorStyle::PointingHand) .when(self.hover_feedback, |control| { - control.hover(move |mut style| { + control.hover_style(move |mut style| { style.refine(&hover); style }) }) - .active(move |mut style| { + .active_style(move |mut style| { style.refine(&pressed); style }) diff --git a/crates/gitcomet-ui-kit/src/test_support.rs b/crates/gitcomet-ui-kit/src/test_support.rs index 8503ec1f7..219ceb57b 100644 --- a/crates/gitcomet-ui-kit/src/test_support.rs +++ b/crates/gitcomet-ui-kit/src/test_support.rs @@ -355,7 +355,8 @@ pub mod source_guards { src_dir: &Path, allow: &DiscreteControlAllowlist<'_>, ) -> Vec { - let pattern = regex::Regex::new(r"\.(?:hover|active)\(\s*(?:move\s+)?\|").unwrap(); + let pattern = + regex::Regex::new(r"\.(?:hover|active)(?:_style)?\(\s*(?:move\s+)?\|").unwrap(); let raw_click = regex::Regex::new(r"\.on_click\(\s*(?:cx\.listener|on_click\b|(?:move\s+)?\|)") .unwrap(); @@ -439,6 +440,40 @@ pub mod source_guards { #[cfg(test)] mod tests { + #[test] + fn old_and_current_hover_press_methods_require_shared_ownership() { + let sources = tempfile::tempdir().unwrap(); + for method in ["hover", "hover_style", "active", "active_style"] { + std::fs::write( + sources.path().join("control.rs"), + format!("div().{method}(move |style| style.bg(color))"), + ) + .unwrap(); + let allow = super::DiscreteControlAllowlist { + custom_styling: &[], + release_gestures: &[], + primary_press: &[], + context_press: &[], + }; + assert_eq!( + super::discrete_control_violations(sources.path(), &allow), + ["control.rs: custom hover/press styling"], + "{method} must go through shared interaction styling" + ); + assert!( + super::discrete_control_violations( + sources.path(), + &super::DiscreteControlAllowlist { + custom_styling: &["control.rs"], + ..allow + }, + ) + .is_empty(), + "{method} keeps its ownership exemption necessary" + ); + } + } + #[test] fn allowlist_entries_that_are_missing_or_unneeded_are_violations() { let sources = tempfile::tempdir().unwrap(); diff --git a/crates/gitcomet-ui-kit/src/text_input/render.rs b/crates/gitcomet-ui-kit/src/text_input/render.rs index f09daa986..bd1939d8d 100644 --- a/crates/gitcomet-ui-kit/src/text_input/render.rs +++ b/crates/gitcomet-ui-kit/src/text_input/render.rs @@ -315,10 +315,10 @@ impl Render for TextInput { } else { input = input .border_color(style.border) - .hover(move |s| s.border_color(style.hover_border)); + .hover_style(move |s| s.border_color(style.hover_border)); } - input = input.focus(move |s| s.border_color(style.focus_border)); + input = input.focus_style(move |s| s.border_color(style.focus_border)); } let mut outer = div().id(ElementId::from(("text_input_root", entity_id))); diff --git a/crates/gitcomet-ui-kit/src/window_chrome/mod.rs b/crates/gitcomet-ui-kit/src/window_chrome/mod.rs index a8a43c59f..3462c160f 100644 --- a/crates/gitcomet-ui-kit/src/window_chrome/mod.rs +++ b/crates/gitcomet-ui-kit/src/window_chrome/mod.rs @@ -268,7 +268,7 @@ pub fn titlebar_control_button( .h(icon_size) .flex_shrink_0() .text_color(idle_color) - .group_hover(id, move |s| s.text_color(hover_color)), + .group_hover_style(id, move |s| s.text_color(hover_color)), ), ), ) diff --git a/docs/linux-renderer.md b/docs/linux-renderer.md new file mode 100644 index 000000000..57edb7f9d --- /dev/null +++ b/docs/linux-renderer.md @@ -0,0 +1,36 @@ +# Linux graphics renderer + +GitComet defaults to **Automatic** on Linux, using Vulkan when available and +otherwise trying OpenGL. Choose **GitComet Settings → General → Graphics → +Graphics renderer** to select **Automatic**, **Vulkan**, or **OpenGL**. +Choosing Vulkan or OpenGL requires that backend; it does not fall back to the other. + +Changes are saved before GitComet offers **Restart now** or **Later**. Restart +uses the normal unsaved-file, terminal, Git-operation, and extension guards. +Canceling a guard keeps the new preference saved and cancels the restart. +Focused Git diff/mergetool invocations apply changes on their next launch so +Git's waiting command is not ended by a settings restart. + +The settings show the active backend separately from the saved preference. +Changing the preference does not switch the running renderer. Linux and Windows +renderer preferences are stored separately in the session file. + +For diagnostics, `WGPU_BACKEND=vulkan` or `WGPU_BACKEND=gl` overrides the saved +preference. An override disables the settings choices without changing the +saved preference. Comma-separated overrides retain the existing Vulkan-first +selection order. Unset `WGPU_BACKEND` to use the saved preference again. + +If a saved Vulkan or OpenGL choice prevents startup, close GitComet and set +`"linux_renderer": "auto"` in your `session.json`, or remove that key. +Preserve the other session data. GitComet normally uses +`$XDG_STATE_HOME/gitcomet/session.json`, falling back to +`~/.local/state/gitcomet/session.json`. If `GITCOMET_SESSION_FILE` is set, edit +that file instead. Launch without `WGPU_BACKEND` to use Automatic again. + +OpenGL can fail even when the driver supports it. On Linux, wgpu's +[EGL configuration selection](https://github.com/gfx-rs/wgpu/blob/v30.0.1/wgpu-hal/src/gles/egl.rs#L99-L163) +requires a native-renderable window configuration. Some NVIDIA EGL +configurations do not provide one, leaving no compatible surface formats and +producing `No GPU adapter found that can configure the display surface`. +Use **Automatic** or **Vulkan**; if the app cannot open, reset the saved choice +as described above. diff --git a/scripts/generate-open-source-licenses.sh b/scripts/generate-open-source-licenses.sh index 4b80e7160..b06f1a231 100755 --- a/scripts/generate-open-source-licenses.sh +++ b/scripts/generate-open-source-licenses.sh @@ -39,10 +39,7 @@ BUNDLED_ASSET_LICENSE_ROWS=( # Crates whose manifest names a licence file instead of an SPDX id, keyed by # name@version so an upgrade has to confirm the licence again. LICENSE_OVERRIDES='{ - "tree-sitter-newick@1.1.0": "CECILL-C", - "wgsl-rs@0.1.0": "MIT", - "wgsl-rs-ir@0.1.0": "MIT", - "wgsl-rs-macros@0.1.0": "MIT" + "tree-sitter-newick@1.1.0": "CECILL-C" }' cleanup() {