From e92c87061d2c62529141106e346c31f2e4a2008e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 8 Sep 2025 09:49:11 +0200 Subject: [PATCH] Add `linux-theme-detection` feature through `mundy` --- Cargo.lock | 477 ++++++++++++++++++++++++++++++++++++-- Cargo.toml | 7 +- runtime/src/lib.rs | 8 + winit/Cargo.toml | 5 + winit/src/conversion.rs | 29 ++- winit/src/lib.rs | 57 ++++- winit/src/window.rs | 3 +- winit/src/window/state.rs | 30 ++- 8 files changed, 566 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d8d8bcba..fad67bc7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,6 +91,15 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "android-build" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cac4c64175d504608cf239756339c07f6384a476f97f20a7043f92920b0b8fd" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "android-properties" version = "0.2.2" @@ -190,6 +199,18 @@ dependencies = [ "libloading", ] +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-channel" version = "2.5.0" @@ -285,6 +306,17 @@ dependencies = [ "rustix 1.0.8", ] +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-signal" version = "0.2.12" @@ -309,6 +341,17 @@ version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-tungstenite" version = "0.25.1" @@ -502,6 +545,15 @@ dependencies = [ "objc2 0.5.2", ] +[[package]] +name = "block2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" +dependencies = [ + "objc2 0.6.2", +] + [[package]] name = "blocking" version = "1.6.2" @@ -779,7 +831,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b7f4aaa047ba3c3630b080bb9860894732ff23e2aee290a418909aa6d5df38f" dependencies = [ "objc2 0.5.2", - "objc2-app-kit", + "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", ] @@ -1189,6 +1241,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1300,6 +1362,33 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "equator" version = "0.4.2" @@ -2093,6 +2182,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hexf-parse" version = "0.2.1" @@ -2512,6 +2607,7 @@ dependencies = [ "iced_debug", "iced_program", "log", + "mundy", "rustc-hash 2.1.1", "sysinfo", "thiserror 1.0.69", @@ -3162,6 +3258,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "metal" version = "0.32.0" @@ -3271,6 +3376,31 @@ dependencies = [ "voronator", ] +[[package]] +name = "mundy" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f507e52285e981a349f7224e0ac7eaf014e1a9cce399471547c8dfbc018b79" +dependencies = [ + "android-build", + "async-io", + "cfg-if", + "dispatch", + "futures-channel", + "futures-lite", + "jni", + "ndk-context", + "objc2 0.6.2", + "objc2-app-kit 0.3.1", + "objc2-foundation 0.3.1", + "pin-project-lite", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows 0.61.3", + "zbus", +] + [[package]] name = "mutate_once" version = "0.1.2" @@ -3356,6 +3486,19 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.9.4", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + [[package]] name = "nom" version = "7.1.3" @@ -3531,13 +3674,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ "bitflags 2.9.4", - "block2", + "block2 0.5.1", "libc", "objc2 0.5.2", - "objc2-core-data", - "objc2-core-image", + "objc2-core-data 0.2.2", + "objc2-core-image 0.2.2", "objc2-foundation 0.2.2", - "objc2-quartz-core", + "objc2-quartz-core 0.2.2", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" +dependencies = [ + "bitflags 2.9.4", + "block2 0.6.1", + "libc", + "objc2 0.6.2", + "objc2-cloud-kit 0.3.1", + "objc2-core-data 0.3.1", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image 0.3.1", + "objc2-foundation 0.3.1", + "objc2-quartz-core 0.3.1", ] [[package]] @@ -3547,19 +3709,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ "bitflags 2.9.4", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-cloud-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17614fdcd9b411e6ff1117dfb1d0150f908ba83a7df81b1f118005fe0a8ea15d" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.2", + "objc2-foundation 0.3.1", +] + [[package]] name = "objc2-contacts" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -3571,30 +3744,75 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.9.4", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-core-data" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291fbbf7d29287518e8686417cf7239c74700fd4b607623140a7d4a3c834329d" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.2", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +dependencies = [ + "bitflags 2.9.4", + "dispatch2", + "objc2 0.6.2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" +dependencies = [ + "bitflags 2.9.4", + "dispatch2", + "objc2 0.6.2", + "objc2-core-foundation", + "objc2-io-surface", +] + [[package]] name = "objc2-core-image" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-metal", ] +[[package]] +name = "objc2-core-image" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79b3dc0cc4386b6ccf21c157591b34a7f44c8e75b064f85502901ab2188c007e" +dependencies = [ + "objc2 0.6.2", + "objc2-foundation 0.3.1", +] + [[package]] name = "objc2-core-location" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-contacts", "objc2-foundation 0.2.2", @@ -3613,7 +3831,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.9.4", - "block2", + "block2 0.5.1", "dispatch", "libc", "objc2 0.5.2", @@ -3626,7 +3844,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ "bitflags 2.9.4", + "block2 0.6.1", + "libc", "objc2 0.6.2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.2", + "objc2-core-foundation", ] [[package]] @@ -3635,9 +3867,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", - "objc2-app-kit", + "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", ] @@ -3648,7 +3880,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.9.4", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -3660,12 +3892,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.9.4", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-metal", ] +[[package]] +name = "objc2-quartz-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.2", + "objc2-foundation 0.3.1", +] + [[package]] name = "objc2-symbols" version = "0.2.2" @@ -3683,15 +3926,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ "bitflags 2.9.4", - "block2", + "block2 0.5.1", "objc2 0.5.2", - "objc2-cloud-kit", - "objc2-core-data", - "objc2-core-image", + "objc2-cloud-kit 0.2.2", + "objc2-core-data 0.2.2", + "objc2-core-image 0.2.2", "objc2-core-location", "objc2-foundation 0.2.2", "objc2-link-presentation", - "objc2-quartz-core", + "objc2-quartz-core 0.2.2", "objc2-symbols", "objc2-uniform-type-identifiers", "objc2-user-notifications", @@ -3703,7 +3946,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -3715,7 +3958,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ "bitflags 2.9.4", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", @@ -3852,6 +4095,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "ouroboros" version = "0.18.5" @@ -4962,6 +5215,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serde_spanned" version = "0.6.9" @@ -5215,7 +5479,7 @@ dependencies = [ "memmap2", "objc2 0.5.2", "objc2-foundation 0.2.2", - "objc2-quartz-core", + "objc2-quartz-core 0.2.2", "raw-window-handle 0.6.2", "redox_syscall 0.5.17", "rustix 0.38.44", @@ -6002,6 +6266,17 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + [[package]] name = "unicase" version = "2.8.1" @@ -6780,6 +7055,28 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + [[package]] name = "windows-core" version = "0.57.0" @@ -6818,6 +7115,17 @@ dependencies = [ "windows-strings 0.4.2", ] +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", +] + [[package]] name = "windows-implement" version = "0.57.0" @@ -6896,6 +7204,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + [[package]] name = "windows-registry" version = "0.5.3" @@ -7070,6 +7388,15 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -7259,7 +7586,7 @@ dependencies = [ "android-activity", "atomic-waker", "bitflags 2.9.4", - "block2", + "block2 0.5.1", "bytemuck", "calloop", "cfg_aliases", @@ -7273,7 +7600,7 @@ dependencies = [ "memmap2", "ndk", "objc2 0.5.2", - "objc2-app-kit", + "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", "objc2-ui-kit", "orbclient", @@ -7436,6 +7763,66 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zbus" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a073be99ace1adc48af593701c8015cd9817df372e14a1a6b0ee8f8bf043be" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "nix", + "ordered-stream", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "windows-sys 0.60.2", + "winnow", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e80cd713a45a49859dcb648053f63265f4f2851b6420d47a958e5697c68b131" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" +dependencies = [ + "serde", + "static_assertions", + "winnow", + "zvariant", +] + [[package]] name = "zeno" version = "0.3.3" @@ -7545,3 +7932,43 @@ checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" dependencies = [ "zune-core", ] + +[[package]] +name = "zvariant" +version = "5.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "999dd3be73c52b1fccd109a4a81e4fcd20fab1d3599c8121b38d04e1419498db" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6643fd0b26a46d226bd90d3f07c1b5321fe9bb7f04673cb37ac6d6883885b68e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn", + "winnow", +] diff --git a/Cargo.toml b/Cargo.toml index 8e952b41..157c59bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ all-features = true maintenance = { status = "actively-developed" } [features] -default = ["wgpu", "tiny-skia", "crisp", "web-colors", "thread-pool"] +default = ["wgpu", "tiny-skia", "crisp", "web-colors", "thread-pool", "linux-theme-detection"] # Enables the `wgpu` GPU-accelerated renderer backend wgpu = ["iced_renderer/wgpu", "iced_widget/wgpu"] # Enables the `tiny-skia` software renderer backend @@ -61,7 +61,7 @@ web-colors = ["iced_renderer/web-colors"] crisp = ["iced_core/crisp", "iced_widget/crisp"] # Enables the WebGL backend webgl = ["iced_renderer/webgl"] -# Enables syntax highligthing +# Enables syntax highlighting highlighter = ["iced_highlighter", "iced_widget/highlighter"] # Enables the advanced module advanced = ["iced_core/advanced", "iced_widget/advanced"] @@ -77,6 +77,8 @@ strict-assertions = ["iced_renderer/strict-assertions"] unconditional-rendering = ["iced_winit/unconditional-rendering"] # Enables support for the `sipper` library sipper = ["iced_runtime/sipper"] +# Enables Linux system theme detection +linux-theme-detection = ["iced_winit/linux-theme-detection"] [dependencies] iced_debug.workspace = true @@ -185,6 +187,7 @@ lilt = "0.8" log = "0.4" lyon = "1.0" lyon_path = "1.0" +mundy = { version = "0.2", default-features = false } num-traits = "0.2" ouroboros = "0.18" png = "0.18" diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 457f723c..77e80b8d 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -25,6 +25,7 @@ pub use iced_futures as futures; pub use task::Task; pub use user_interface::UserInterface; +use crate::core::theme; use crate::core::widget; use crate::futures::futures::channel::oneshot; @@ -56,6 +57,9 @@ pub enum Action { /// Run a system action. System(system::Action), + /// Change the default system theme mode of all windows. + ChangeTheme(theme::Mode), + /// Recreate all user interfaces and redraw all windows. Reload, @@ -82,6 +86,7 @@ impl Action { Action::Clipboard(action) => Err(Action::Clipboard(action)), Action::Window(action) => Err(Action::Window(action)), Action::System(action) => Err(Action::System(action)), + Action::ChangeTheme(mode) => Err(Action::ChangeTheme(mode)), Action::Reload => Err(Action::Reload), Action::Exit => Err(Action::Exit), } @@ -106,6 +111,9 @@ where } Action::Window(_) => write!(f, "Action::Window"), Action::System(action) => write!(f, "Action::System({action:?})"), + Action::ChangeTheme(mode) => { + write!(f, "Action::ChangeTheme({mode:?})") + } Action::Reload => write!(f, "Action::Reload"), Action::Exit => write!(f, "Action::Exit"), } diff --git a/winit/Cargo.toml b/winit/Cargo.toml index f2157978..85390a0d 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -23,6 +23,7 @@ wayland = ["winit/wayland"] wayland-dlopen = ["winit/wayland-dlopen"] wayland-csd-adwaita = ["winit/wayland-csd-adwaita"] unconditional-rendering = [] +linux-theme-detection = ["dep:mundy", "mundy/async-io", "mundy/color-scheme"] [dependencies] iced_debug.workspace = true @@ -42,3 +43,7 @@ sysinfo.optional = true web-sys.workspace = true web-sys.features = ["Document", "Window", "HtmlCanvasElement"] wasm-bindgen-futures.workspace = true + +[target.'cfg(target_os = "linux")'.dependencies] +mundy.workspace = true +mundy.optional = true diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index 8668d6a3..feef78bd 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -326,7 +326,7 @@ pub fn window_event( } } -/// Converts a [`window::Level`] to a [`winit`] window level. +/// Converts a [`window::Level`] into a [`winit`] window level. /// /// [`winit`]: https://github.com/rust-windowing/winit pub fn window_level(level: window::Level) -> winit::window::WindowLevel { @@ -339,7 +339,7 @@ pub fn window_level(level: window::Level) -> winit::window::WindowLevel { } } -/// Converts a [`window::Position`] to a [`winit`] logical position for a given monitor. +/// Converts a [`window::Position`] into a [`winit`] logical position for a given monitor. /// /// [`winit`]: https://github.com/rust-windowing/winit pub fn position( @@ -411,7 +411,7 @@ pub fn position( } } -/// Converts a [`window::Mode`] to a [`winit`] fullscreen mode. +/// Converts a [`window::Mode`] into a [`winit`] fullscreen mode. /// /// [`winit`]: https://github.com/rust-windowing/winit pub fn fullscreen( @@ -426,7 +426,7 @@ pub fn fullscreen( } } -/// Converts a [`window::Mode`] to a visibility flag. +/// Converts a [`window::Mode`] into a visibility flag. pub fn visible(mode: window::Mode) -> bool { match mode { window::Mode::Windowed | window::Mode::Fullscreen => true, @@ -434,7 +434,7 @@ pub fn visible(mode: window::Mode) -> bool { } } -/// Converts a [`winit`] fullscreen mode to a [`window::Mode`]. +/// Converts a [`winit`] fullscreen mode into a [`window::Mode`]. /// /// [`winit`]: https://github.com/rust-windowing/winit pub fn mode(mode: Option) -> window::Mode { @@ -444,7 +444,7 @@ pub fn mode(mode: Option) -> window::Mode { } } -/// Converts a [`winit`] window theme to a [`theme::Mode`]. +/// Converts a [`winit`] window theme into a [`theme::Mode`]. /// /// [`winit`]: https://github.com/rust-windowing/winit pub fn theme_mode(theme: winit::window::Theme) -> theme::Mode { @@ -454,7 +454,18 @@ pub fn theme_mode(theme: winit::window::Theme) -> theme::Mode { } } -/// Converts a [`mouse::Interaction`] to a [`winit`] cursor icon. +/// Converts a [`theme::Mode`] into a window theme. +/// +/// [`winit`]: https://github.com/rust-windowing/winit +pub fn window_theme(mode: theme::Mode) -> Option { + match mode { + theme::Mode::None => None, + theme::Mode::Light => Some(winit::window::Theme::Light), + theme::Mode::Dark => Some(winit::window::Theme::Dark), + } +} + +/// Converts a [`mouse::Interaction`] into a [`winit`] cursor icon. /// /// [`winit`]: https://github.com/rust-windowing/winit pub fn mouse_interaction( @@ -525,7 +536,7 @@ pub fn modifiers( result } -/// Converts a physical cursor position to a logical `Point`. +/// Converts a physical cursor position into a logical `Point`. pub fn cursor_position( position: winit::dpi::PhysicalPosition, scale_factor: f32, @@ -1196,7 +1207,7 @@ pub fn icon(icon: window::Icon) -> Option { winit::window::Icon::from_rgba(pixels, size.width, size.height).ok() } -/// Convertions some [`input_method::Purpose`] to its `winit` counterpart. +/// Convertions some [`input_method::Purpose`] into its `winit` counterpart. pub fn ime_purpose( purpose: input_method::Purpose, ) -> winit::window::ImePurpose { diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 6f9cea56..a2d58820 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -111,7 +111,7 @@ where let (_id, open) = runtime::window::open(window_settings); - open.then(move |_| task.take().unwrap_or(Task::none())) + open.then(move |_| task.take().unwrap_or_else(Task::none)) } else { task }; @@ -517,6 +517,33 @@ async fn run_instance

( let mut user_interfaces = ManuallyDrop::new(FxHashMap::default()); let mut clipboard = Clipboard::unconnected(); + #[cfg(all(feature = "linux-theme-detection", target_os = "linux"))] + let system_theme = { + let to_mode = |color_scheme| match color_scheme { + mundy::ColorScheme::NoPreference => theme::Mode::None, + mundy::ColorScheme::Light => theme::Mode::Light, + mundy::ColorScheme::Dark => theme::Mode::Dark, + }; + + runtime.run( + mundy::Preferences::stream(mundy::Interest::ColorScheme) + .map(move |preferences| { + Action::ChangeTheme(to_mode(preferences.color_scheme)) + }) + .boxed(), + ); + + mundy::Preferences::once_blocking( + mundy::Interest::ColorScheme, + core::time::Duration::from_millis(200), + ) + .map(|preferences| to_mode(preferences.color_scheme)) + .unwrap_or_default() + }; + + #[cfg(not(all(feature = "linux-theme-detection", target_os = "linux")))] + let system_theme = theme::Mode::None; + loop { // Empty the queue if possible let event = if let Ok(event) = event_receiver.try_next() { @@ -604,6 +631,7 @@ async fn run_instance

( .as_mut() .expect("Compositor must be initialized"), exit_on_close_request, + system_theme, ); debug::theme_changed(|| { @@ -891,7 +919,11 @@ async fn run_instance

( &mut is_window_opening, ); } else { - window.state.update(&window.raw, &window_event); + window.state.update( + &program, + &window.raw, + &window_event, + ); if let Some(event) = conversion::window_event( window_event, @@ -1465,6 +1497,27 @@ fn run_action<'a, P, C>( let _ = channel.send(Ok(())); } } + Action::ChangeTheme(mode) => { + let Some(theme) = conversion::window_theme(mode) else { + return; + }; + + for (id, window) in window_manager.iter_mut() { + window.raw.set_theme(Some(theme)); + window.state.update( + program, + &window.raw, + &winit::event::WindowEvent::ThemeChanged(theme), + ); + + events.push(( + id, + core::Event::Window(core::window::Event::ThemeModeChanged( + mode, + )), + )); + } + } Action::Reload => { for (id, window) in window_manager.iter_mut() { let Some(ui) = interfaces.remove(&id) else { diff --git a/winit/src/window.rs b/winit/src/window.rs index f2d12677..419c50e1 100644 --- a/winit/src/window.rs +++ b/winit/src/window.rs @@ -55,8 +55,9 @@ where program: &program::Instance

, compositor: &mut C, exit_on_close_request: bool, + system_theme: theme::Mode, ) -> &mut Window { - let state = State::new(program, id, &window); + let state = State::new(program, id, &window, system_theme); let viewport_version = state.viewport_version(); let physical_size = state.physical_size(); let surface = compositor.create_surface( diff --git a/winit/src/window/state.rs b/winit/src/window/state.rs index 65f08d21..b3f9af3c 100644 --- a/winit/src/window/state.rs +++ b/winit/src/window/state.rs @@ -21,7 +21,7 @@ where cursor_position: Option>, modifiers: winit::keyboard::ModifiersState, theme: Option, - system_theme: P::Theme, + default_theme: P::Theme, style: theme::Style, } @@ -50,16 +50,18 @@ where program: &program::Instance

, window_id: window::Id, window: &Window, + system_theme: theme::Mode, ) -> Self { - let title = program.title(window_id); - let scale_factor = program.scale_factor(window_id); let theme_mode = window .theme() .map(conversion::theme_mode) - .unwrap_or_default(); + .unwrap_or(system_theme); + + let title = program.title(window_id); + let scale_factor = program.scale_factor(window_id); let theme = program.theme(window_id); - let system_theme = ::default(theme_mode); - let style = program.style(theme.as_ref().unwrap_or(&system_theme)); + let default_theme = ::default(theme_mode); + let style = program.style(theme.as_ref().unwrap_or(&default_theme)); let viewport = { let physical_size = window.inner_size(); @@ -78,7 +80,7 @@ where cursor_position: None, modifiers: winit::keyboard::ModifiersState::default(), theme, - system_theme, + default_theme, style, } } @@ -130,7 +132,7 @@ where /// Returns the current theme of the [`State`]. pub fn theme(&self) -> &P::Theme { - self.theme.as_ref().unwrap_or(&self.system_theme) + self.theme.as_ref().unwrap_or(&self.default_theme) } /// Returns the current background [`Color`] of the [`State`]. @@ -144,7 +146,12 @@ where } /// Processes the provided window event and updates the [`State`] accordingly. - pub fn update(&mut self, window: &Window, event: &WindowEvent) { + pub fn update( + &mut self, + program: &program::Instance

, + window: &Window, + event: &WindowEvent, + ) { match event { WindowEvent::Resized(new_size) => { let size = Size::new(new_size.width, new_size.height); @@ -182,9 +189,10 @@ where self.modifiers = new_modifiers.state(); } WindowEvent::ThemeChanged(theme) => { - self.system_theme = ::default( + self.default_theme = ::default( conversion::theme_mode(*theme), ); + self.style = program.style(self.theme()); if self.theme.is_none() { window.request_redraw(); @@ -198,7 +206,7 @@ where /// window. /// /// Normally, a [`Program`] should be synchronized with its [`State`] - /// and window after calling [`State::update`]. + /// and window after calling [`Program::update`]. pub fn synchronize( &mut self, program: &program::Instance

,