diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index eb3a06b..ed0c200 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install GTK4 - run: sudo apt update && sudo apt install libgtk-4-dev build-essential + run: sudo apt update && sudo apt install libgtk-4-dev build-essential libadwaita-1-dev - run: rustup toolchain install 1.75 --profile minimal - name: Restore build cache uses: Swatinem/rust-cache@v2.7.5 diff --git a/Cargo.lock b/Cargo.lock index b386e60..35a635e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,20 +2,11 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" @@ -32,12 +23,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -49,9 +34,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -64,43 +49,50 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", - "windows-sys 0.59.0", + "once_cell_polyfill", + "windows-sys 0.60.2", ] +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", @@ -109,48 +101,57 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] -name = "backtrace" -version = "0.3.74" +name = "basic-toml" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets", + "serde", ] [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cairo-rs" -version = "0.20.7" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae50b5510d86cf96ac2370e66d8dc960882f3df179d6a5a1e52bd94a1416c0f7" +checksum = "dfe4354df4da648870e363387679081f8f9fc538ec8b55901e3740c6a0ef81b1" dependencies = [ "bitflags", "cairo-sys-rs", @@ -160,9 +161,9 @@ dependencies = [ [[package]] name = "cairo-sys-rs" -version = "0.20.7" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f18b6bb8e43c7eb0f2aac7976afe0c61b6f5fc2ab7bc4c139537ea56c92290df" +checksum = "47d6c3300c7103eb8e4de07591003511aa25664438f8c6fc317a3a9902c103f8" dependencies = [ "glib-sys", "libc", @@ -171,18 +172,19 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.6" +version = "1.2.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" +checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" dependencies = [ + "find-msvc-tools", "shlex", ] [[package]] name = "cfg-expr" -version = "0.17.2" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d4ba6e40bd1184518716a6e1a781bf9160e286d219ccdb8ab2612e74cfe4789" +checksum = "1a2c5f3bf25ec225351aa1c8e230d04d880d3bd89dea133537dafad4ae291e5c" dependencies = [ "smallvec", "target-lexicon", @@ -190,27 +192,26 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" -version = "0.4.39" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "num-traits", - "windows-targets", + "windows-link", ] [[package]] name = "clap" -version = "4.5.23" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +checksum = "f4512b90fa68d3a9932cea5184017c5d200f5921df706d45e853537dea51508f" dependencies = [ "clap_builder", "clap_derive", @@ -218,9 +219,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.23" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +checksum = "0025e98baa12e766c67ba13ff4695a887a1eba19569aad00a472546795bd6730" dependencies = [ "anstream", "anstyle", @@ -230,9 +231,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", @@ -242,21 +243,21 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "const_format" -version = "0.2.34" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" dependencies = [ "const_format_proc_macros", "konst", @@ -279,11 +280,20 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -303,15 +313,46 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "deranged" -version = "0.3.11" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" dependencies = [ "powerfmt", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "educe" version = "0.6.0" @@ -346,9 +387,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "field-offset" @@ -370,16 +411,75 @@ dependencies = [ "flate2", ] +[[package]] +name = "find-crate" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2" +dependencies = [ + "toml 0.5.11", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + [[package]] name = "flate2" -version = "1.0.35" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9" dependencies = [ "crc32fast", "miniz_oxide", ] +[[package]] +name = "fluent" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb74634707bebd0ce645a981148e8fb8c7bccd4c33c652aeffd28bf2f96d555a" +dependencies = [ + "fluent-bundle", + "unic-langid", +] + +[[package]] +name = "fluent-bundle" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "rustc-hash 1.1.0", + "self_cell 0.10.3", + "smallvec", + "unic-langid", +] + +[[package]] +name = "fluent-langneg" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "fluent-syntax" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" +dependencies = [ + "thiserror 1.0.69", +] + [[package]] name = "flume" version = "0.11.1" @@ -394,15 +494,15 @@ dependencies = [ [[package]] name = "foldhash" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "fragile" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" +checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" [[package]] name = "futures" @@ -495,9 +595,9 @@ dependencies = [ [[package]] name = "gdk-pixbuf" -version = "0.20.7" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6efc7705f7863d37b12ad6974cbb310d35d054f5108cdc1e69037742f573c4c" +checksum = "2a3c64459f569154f37616fc28923bfac490d4aaa134aaf5eca58a2c0c13050f" dependencies = [ "gdk-pixbuf-sys", "gio", @@ -507,9 +607,9 @@ dependencies = [ [[package]] name = "gdk-pixbuf-sys" -version = "0.20.7" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67f2587c9202bf997476bbba6aaed4f78a11538a2567df002a5f57f5331d0b5c" +checksum = "3854ef7a6a8b8f3b4013a01d5f9cb0d1794ec4e810c6cb4e2cc6d980f1baf724" dependencies = [ "gio-sys", "glib-sys", @@ -520,9 +620,9 @@ dependencies = [ [[package]] name = "gdk4" -version = "0.9.5" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0196720118f880f71fe7da971eff58cc43a89c9cf73f46076b7cb1e60889b15" +checksum = "c7e292649dc26e3440c508a00f42ab39156008320dd6e962d63eaf626ba4d7f0" dependencies = [ "cairo-rs", "gdk-pixbuf", @@ -535,9 +635,9 @@ dependencies = [ [[package]] name = "gdk4-sys" -version = "0.9.5" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b0e1340bd15e7a78810cf39fed9e5d85f0a8f80b1d999d384ca17dcc452b60" +checksum = "f4f3174fa4f1e0bf2a7e04469b65db8f4d1db89a6f5cdc57727b14e97ce438cf" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -550,11 +650,21 @@ dependencies = [ "system-deps", ] +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", @@ -563,17 +673,11 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - [[package]] name = "gio" -version = "0.20.7" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a517657589a174be9f60c667f1fec8b7ac82ed5db4ebf56cf073a3b5955d8e2e" +checksum = "ed68efc12b748a771be2dccc49480d8584004382967c98323245fc3c38b74a42" dependencies = [ "futures-channel", "futures-core", @@ -588,22 +692,22 @@ dependencies = [ [[package]] name = "gio-sys" -version = "0.20.8" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8446d9b475730ebef81802c1738d972db42fde1c5a36a627ebc4d665fc87db04" +checksum = "171ed2f6dd927abbe108cfd9eebff2052c335013f5879d55bab0dc1dee19b706" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "glib" -version = "0.20.7" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f969edf089188d821a30cde713b6f9eb08b20c63fc2e584aba2892a7984a8cc0" +checksum = "e1f2cbc4577536c849335878552f42086bfd25a8dcd6f54a18655cf818b20c8f" dependencies = [ "bitflags", "futures-channel", @@ -622,9 +726,9 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.20.7" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715601f8f02e71baef9c1f94a657a9a77c192aea6097cf9ae7e5e177cd8cde68" +checksum = "55eda916eecdae426d78d274a17b48137acdca6fba89621bd3705f2835bc719f" dependencies = [ "heck", "proc-macro-crate", @@ -635,9 +739,9 @@ dependencies = [ [[package]] name = "glib-sys" -version = "0.20.7" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b360ff0f90d71de99095f79c526a5888c9c92fc9ee1b19da06c6f5e75f0c2a53" +checksum = "d09d3d0fddf7239521674e57b0465dfbd844632fec54f059f7f56112e3f927e1" dependencies = [ "libc", "system-deps", @@ -645,15 +749,15 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "gobject-sys" -version = "0.20.7" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a56235e971a63bfd75abb13ef70064e1346388723422a68580d8a6fbac6423" +checksum = "538e41d8776173ec107e7b0f2aceced60abc368d7e1d81c1f0e2ecd35f59080d" dependencies = [ "glib-sys", "libc", @@ -662,9 +766,9 @@ dependencies = [ [[package]] name = "graphene-rs" -version = "0.20.7" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f39d3bcd2e24fd9c2874a56f277b72c03e728de9bdc95a8d4ef4c962f10ced98" +checksum = "e7749aaf5d3b955bf3bfce39e3423705878a666b561384134da0e7786a45ddc3" dependencies = [ "glib", "graphene-sys", @@ -673,9 +777,9 @@ dependencies = [ [[package]] name = "graphene-sys" -version = "0.20.7" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a68d39515bf340e879b72cecd4a25c1332557757ada6e8aba8654b4b81d23a" +checksum = "250abaee850a90a276509890a78029c356173f9573412bded5f155b0e41fa568" dependencies = [ "glib-sys", "libc", @@ -698,9 +802,9 @@ dependencies = [ [[package]] name = "gsk4" -version = "0.9.5" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b9188db0a6219e708b6b6e7225718e459def664023dbddb8395ca1486d8102" +checksum = "b6687e9f92ca89c000c376400cfaf7914d099413d72fdf4f84a25775a0b1fb2d" dependencies = [ "cairo-rs", "gdk4", @@ -713,9 +817,9 @@ dependencies = [ [[package]] name = "gsk4-sys" -version = "0.9.5" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca10fc65d68528a548efa3d8747934adcbe7058b73695c9a7f43a25352fce14" +checksum = "5e76bcf64d9c4846f19651f45b400cc0c9c4c17b651849da520f3d77c6988c52" dependencies = [ "cairo-sys-rs", "gdk4-sys", @@ -729,9 +833,9 @@ dependencies = [ [[package]] name = "gtk4" -version = "0.9.5" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b697ff938136625f6acf75f01951220f47a45adcf0060ee55b4671cf734dac44" +checksum = "8f7887ee0ceeffedb25a418810a2c61497dacad51767fc13f9d60859b4023b8a" dependencies = [ "cairo-rs", "field-offset", @@ -750,9 +854,9 @@ dependencies = [ [[package]] name = "gtk4-macros" -version = "0.9.5" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed1786c4703dd196baf7e103525ce0cf579b3a63a0570fe653b7ee6bac33999" +checksum = "821160b4f17e7e4ed748818c23682d0a46bed04c287dbaac54dd4869d2c5e06a" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -762,9 +866,9 @@ dependencies = [ [[package]] name = "gtk4-sys" -version = "0.9.5" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3af4b680cee5d2f786a2f91f1c77e95ecf2254522f0ca4edf3a2dce6cb35cecf" +checksum = "d274cbaf7d9aa55b7aff78cb21b43299d64e514e1300671469b66f691cc5a011" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -781,15 +885,21 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", "foldhash", ] +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + [[package]] name = "heck" version = "0.5.0" @@ -798,9 +908,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "humantime" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "humantime-serde" @@ -812,16 +922,84 @@ dependencies = [ "serde", ] +[[package]] +name = "i18n-config" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e06b90c8a0d252e203c94344b21e35a30f3a3a85dc7db5af8f8df9f3e0c63ef" +dependencies = [ + "basic-toml", + "log", + "serde", + "serde_derive", + "thiserror 1.0.69", + "unic-langid", +] + +[[package]] +name = "i18n-embed" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "669ffc2c93f97e6ddf06ddbe999fcd6782e3342978bb85f7d3c087c7978404c4" +dependencies = [ + "arc-swap", + "fluent", + "fluent-langneg", + "fluent-syntax", + "i18n-embed-impl", + "intl-memoizer", + "locale_config", + "log", + "parking_lot", + "rust-embed", + "thiserror 1.0.69", + "unic-langid", + "walkdir", +] + +[[package]] +name = "i18n-embed-fl" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04b2969d0b3fc6143776c535184c19722032b43e6a642d710fa3f88faec53c2d" +dependencies = [ + "find-crate", + "fluent", + "fluent-syntax", + "i18n-config", + "i18n-embed", + "proc-macro-error2", + "proc-macro2", + "quote", + "strsim", + "syn", + "unic-langid", +] + +[[package]] +name = "i18n-embed-impl" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2cc0e0523d1fe6fc2c6f66e5038624ea8091b3e7748b5e8e0c84b1698db6c2" +dependencies = [ + "find-crate", + "i18n-config", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", "windows-core", ] @@ -837,12 +1015,31 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.16.0", +] + +[[package]] +name = "intl-memoizer" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310da2e345f5eb861e7a07ee182262e94975051db9e4223e909ba90f392f163f" +dependencies = [ + "type-map", + "unic-langid", +] + +[[package]] +name = "intl_pluralrules" +version = "7.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972" +dependencies = [ + "unic-langid", ] [[package]] @@ -853,41 +1050,44 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.1.16" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a46169c7a10358cdccfb179910e8a5a392fc291bdb409da9aeece5b19786d8" +checksum = "c04ef77ae73f3cf50510712722f0c4e8b46f5aaa1bf5ffad2ae213e6495e78e5" dependencies = [ "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", "serde", "windows-sys 0.59.0", ] [[package]] name = "jiff-tzdb" -version = "0.1.1" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91335e575850c5c4c673b9bd467b0e025f164ca59d0564f69d0c2ee0ffad4653" +checksum = "c1283705eb0a21404d2bfd6eef2a7593d240bc42a0bdb39db0ad6fa2ec026524" [[package]] name = "jiff-tzdb-platform" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9835f0060a626fe59f160437bc725491a6af23133ea906500027d1bd2f8f4329" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" dependencies = [ "jiff-tzdb", ] [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" dependencies = [ "once_cell", "wasm-bindgen", @@ -914,27 +1114,70 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "libadwaita" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4df6715d1257bd8c093295b77a276ed129d73543b10304fec5829ced5d5b7c41" +dependencies = [ + "gdk4", + "gio", + "glib", + "gtk4", + "libadwaita-sys", + "libc", + "pango", +] + +[[package]] +name = "libadwaita-sys" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf8950090cc180250cdb1ff859a39748feeda7a53a9f28ead3a17a14cc37ae2" +dependencies = [ + "gdk4-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk4-sys", + "libc", + "pango-sys", + "system-deps", +] + [[package]] name = "libc" -version = "0.2.169" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "locale_config" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934" +dependencies = [ + "lazy_static", + "objc", + "objc-foundation", + "regex", + "winapi", +] [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.22" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "lru" @@ -942,14 +1185,23 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown", + "hashbrown 0.15.5", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", ] [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memoffset" @@ -962,22 +1214,23 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.2" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ "libc", "wasi", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -991,11 +1244,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.50.1" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -1023,25 +1276,51 @@ dependencies = [ ] [[package]] -name = "object" -version = "0.36.7" +name = "objc" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" dependencies = [ - "memchr", + "malloc_buf", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", ] [[package]] name = "once_cell" -version = "1.20.2" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "pango" -version = "0.20.7" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e89bd74250a03a05cec047b43465469102af803be2bf5e5a1088f8b8455e087" +checksum = "e37b7a678e18c2e9f2485f7e39b7b2dac99590d5ddef08a7f56eae38a145402e" dependencies = [ "gio", "glib", @@ -1051,9 +1330,9 @@ dependencies = [ [[package]] name = "pango-sys" -version = "0.20.7" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71787e0019b499a5eda889279e4adb455a4f3fdd6870cd5ab7f4a5aa25df6699" +checksum = "f4f5daf21da43fba9f2a0092da0eebeb77637c23552bccaf58f791c518009c94" dependencies = [ "glib-sys", "gobject-sys", @@ -1061,11 +1340,34 @@ dependencies = [ "system-deps", ] +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -1075,58 +1377,104 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] -name = "powerfmt" -version = "0.2.0" +name = "portable-atomic" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] -name = "proc-macro-crate" -version = "3.2.0" +name = "portable-atomic-util" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" dependencies = [ - "toml_edit", + "portable-atomic", ] [[package]] -name = "proc-macro2" -version = "1.0.92" +name = "powerfmt" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" -dependencies = [ - "unicode-ident", -] +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] -name = "pwd" -version = "1.4.0" +name = "proc-macro-crate" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c71c0c79b9701efe4e1e4b563b2016dd4ee789eb99badcb09d61ac4b92e4a2" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "libc", - "thiserror 1.0.69", + "toml_edit 0.23.7", ] [[package]] -name = "quote" -version = "1.0.38" +name = "proc-macro-error-attr2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ "proc-macro2", + "quote", ] [[package]] -name = "regex" -version = "1.11.1" +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pwd" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c71c0c79b9701efe4e1e4b563b2016dd4ee789eb99badcb09d61ac4b92e4a2" +dependencies = [ + "libc", + "thiserror 1.0.69", +] + +[[package]] +name = "quote" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -1136,9 +1484,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -1147,9 +1495,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "regreet" @@ -1163,34 +1511,40 @@ dependencies = [ "greetd_ipc", "gtk4", "humantime-serde", + "i18n-embed", + "i18n-embed-fl", "jiff", "lazy_static", + "libadwaita", "lru", "pwd", "regex", "relm4", + "rust-embed", "serde", "shlex", "test-case", - "thiserror 2.0.9", + "thiserror 2.0.17", "tokio", - "toml", + "toml 0.8.23", "tracing", "tracing-appender", "tracing-subscriber", "tracker", + "unic-langid", ] [[package]] name = "relm4" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30837553c1a8cfea1a404c83ec387c5c8ff9358e1060b057c274c5daa5035ad1" +checksum = "6bae902de22fd92e62641f047975abf228573425b9b8de175e8ab5b6cda10379" dependencies = [ "flume", "fragile", "futures", "gtk4", + "libadwaita", "once_cell", "relm4-css", "relm4-macros", @@ -1200,15 +1554,15 @@ dependencies = [ [[package]] name = "relm4-css" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3b924557df1cddc687b60b313c4b76620fdbf0e463afa4b29f67193ccf37f9" +checksum = "37dbe7a114855a22618f0e13595ce6b3f165478c13c2dfc4f4f99614da105797" [[package]] name = "relm4-macros" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a895a7455441a857d100ca679bd24a92f91d28b5e3df63296792ac1af2eddde" +checksum = "175fce497fc6f11dde7ea56daa30ff7ad29a534bbc209d59d766659c880ba5f1" dependencies = [ "proc-macro2", "quote", @@ -1216,10 +1570,50 @@ dependencies = [ ] [[package]] -name = "rustc-demangle" -version = "0.1.24" +name = "rust-embed" +version = "8.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "fb44e1917075637ee8c7bcb865cf8830e3a92b5b1189e44e3a0ab5a0d5be314b" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382499b49db77a7c19abd2a574f85ada7e9dbe125d5d1160fa5cad7c4cf71fc9" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21fcbee55c2458836bcdbfffb6ec9ba74bbc23ca7aa6816015a3dd2c4d8fc185" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc_version" @@ -1230,11 +1624,26 @@ dependencies = [ "semver", ] +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] [[package]] name = "scopeguard" @@ -1242,26 +1651,51 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "self_cell" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" +dependencies = [ + "self_cell 1.2.0", +] + +[[package]] +name = "self_cell" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" + [[package]] name = "semver" -version = "1.0.24" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.217" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -1270,25 +1704,46 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.134" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +dependencies = [ + "serde_core", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1305,28 +1760,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "slab" -version = "0.4.9" +name = "signal-hook-registry" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ - "autocfg", + "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + [[package]] name = "smallvec" -version = "1.13.2" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.8" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -1346,9 +1813,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.93" +version = "2.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" +checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" dependencies = [ "proc-macro2", "quote", @@ -1357,22 +1824,22 @@ dependencies = [ [[package]] name = "system-deps" -version = "7.0.3" +version = "7.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005" +checksum = "c236d79f20808ca0084bfcd1a2fd6c686216b7f7a0c4fc39deb0cbf5eaab3713" dependencies = [ "cfg-expr", "heck", "pkg-config", - "toml", + "toml 0.9.8", "version-compare", ] [[package]] name = "target-lexicon" -version = "0.12.16" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" [[package]] name = "test-case" @@ -1418,11 +1885,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.9" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.9", + "thiserror-impl 2.0.17", ] [[package]] @@ -1438,9 +1905,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.9" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -1449,19 +1916,18 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] name = "time" -version = "0.3.37" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -1476,69 +1942,146 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.19" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", ] +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tokio" -version = "1.43.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "492a604e2fd7f814268a378409e6c92b5525d747d10db9a229723f55a417958c" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", "libc", "mio", "pin-project-lite", + "signal-hook-registry", "socket2", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "toml" -version = "0.8.19" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned 1.0.3", + "toml_datetime 0.7.3", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.23.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +dependencies = [ + "indexmap", + "toml_datetime 0.7.3", + "toml_parser", "winnow", ] +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "toml_writer" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" + [[package]] name = "tracing" version = "0.1.41" @@ -1564,9 +2107,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", @@ -1575,9 +2118,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -1629,11 +2172,45 @@ dependencies = [ "syn", ] +[[package]] +name = "type-map" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90" +dependencies = [ + "rustc-hash 2.1.1", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unic-langid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05" +dependencies = [ + "unic-langid-impl", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658" +dependencies = [ + "serde", + "tinystr", +] + [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "unicode-xid" @@ -1649,9 +2226,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "version-compare" @@ -1659,28 +2236,46 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" dependencies = [ "bumpalo", "log", @@ -1692,9 +2287,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1702,9 +2297,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", @@ -1715,26 +2310,101 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.52.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-targets", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] -name = "windows-sys" -version = "0.52.0" +name = "windows-implement" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ - "windows-targets", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", ] [[package]] @@ -1743,7 +2413,25 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", ] [[package]] @@ -1752,14 +2440,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -1768,53 +2473,116 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" -version = "0.6.20" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "zerofrom", +] diff --git a/Cargo.toml b/Cargo.toml index 824feae..ab683d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,10 +7,13 @@ name = "regreet" version = "0.1.3" authors = ["Harish Rajagopal "] edition = "2021" -rust-version = "1.75" +rust-version = "1.80" description = "Clean and customizable greeter for greetd" repository = "https://github.com/rharish101/ReGreet/" license = "GPL-3.0-or-later" + +[features] +gtk4_8 = ["gtk4/v4_8"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] @@ -20,26 +23,31 @@ educe = "0.6" file-rotate = "0.7" glob = "0.3" greetd_ipc = { version = "0.10", features = ["tokio-codec"] } -gtk4 = "0.9" +gtk4 = "0.10" humantime-serde = "1.1.1" +i18n-embed = { version = "0.15.2", features = [ + "desktop-requester", + "fluent-system", +] } +i18n-embed-fl = "0.9.2" jiff = "0.1.14" lazy_static = "1.5.0" +libadwaita = { version = "0.8", features = ["v1_6"] } lru = "0.12" pwd = "1.4.0" regex = "1.10" -relm4 = "0.9" +relm4 = { version = "0.10", features = ["adw", "libadwaita"] } +rust-embed = "8.5.0" serde = { version = "1.0", features = ["derive"] } shlex = "1.3" thiserror = "2.0" -tokio = { version = "1.43", features = ["net", "time"] } +tokio = { version = "1.43", features = ["net", "process", "time"] } toml = "0.8" tracing = "0.1" tracing-appender = "0.2" tracing-subscriber = { version = "0.3", features = ["local-time"] } tracker = "0.2" - -[features] -gtk4_8 = ["gtk4/v4_8"] +unic-langid = "0.9.5" [dev-dependencies] test-case = "3.3.1" diff --git a/README.md b/README.md index 86def46..5cc0713 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ These screenshots use the [Canta GTK theme](https://github.com/vinceliuice/Canta - Cursor theme - Cursor blink on/off - Font -* Allows changing reboot & poweroff commands for different init systems +* Power menu: supports `systemd` out-of-the-box, and generic linux `shutdown` command. Support for other init systems is easy to add. * Supports custom CSS files for further customizations * Respects `XDG_DATA_DIRS` environment variable * Respects fields `Hidden` and `NoDisplay` in session files @@ -51,7 +51,7 @@ These screenshots use the [Canta GTK theme](https://github.com/vinceliuice/Canta * Demo mode to run ReGreet without greetd for easier development. ## Requirements -* Rust 1.75.0+ (for compilation only) +* Rust 1.80.0+ (for compilation only) * greetd * GTK 4.0+ * A Wayland compositor (such as [Cage](https://www.hjdskes.nl/projects/cage/) or [Sway](https://swaywm.org/) or [Hyprland](https://hyprland.org/)) @@ -95,8 +95,6 @@ STATE\_DIR | `/var/lib/regreet` | The directory used to store the ReGreet state/ LOG\_DIR | `/var/log/regreet` | The directory used to store logs SESSION\_DIRS | `/usr/share/xsessions:/usr/share/wayland-sessions` | A colon (:) separated list of directories where the greeter looks for session files X11\_CMD\_PREFIX | `startx /usr/bin/env` | The default command prefix for X11 sessions to launch the X server (see [this explanation on Reddit](https://web.archive.org/web/20240803120131/https://old.reddit.com/r/linux/comments/1c8zdcw/using_x11_window_managers_with_greetd_login/)) -REBOOT\_CMD | `reboot` | The default command used to reboot the system -POWEROFF\_CMD | `poweroff` | The default command used to shut down the system LOGIN\_DEFS\_PATHS | `/etc/login.defs:/usr/etc/login.defs` | A colon (:) separated list of `login.defs` file paths. First found is loaded. LOGIN\_DEFS\_UID\_MIN | 1000 | Override the assumed default if `login.defs` doesnt specify `UID_MIN`. LOGIN\_DEFS\_UID\_MAX | 60000 | Override the assumed default if `login.defs` doesnt specify `UID_MAX`. @@ -213,7 +211,7 @@ env = GTK_USE_PORTAL,0 env = GDK_DEBUG,no-portals ``` -### Configuration +## Configuration The configuration file must be in the [TOML](https://toml.io/) format. By default, it is named `regreet.toml`, and located in the greetd configuration directory specified during compilation (`/etc/greetd/` by default). You can use a config file in a different location with the `--config` argument as follows: @@ -234,10 +232,20 @@ Currently, the following can be configured: * Cursor theme * Cursor blink on/off * Font -* Reboot command -* Shut down command +* Power menu * X11 command prefix (see [this explanation on Reddit](https://web.archive.org/web/20240803120131/https://old.reddit.com/r/linux/comments/1c8zdcw/using_x11_window_managers_with_greetd_login/)) +Refer to [the configuration documentation](./docs/CONFIG.md) for more detail. + +### Widgets + +Widgets are additional UI elements that do not interact with `greetd`. Their configuration is under the `[widget]` key. +See `regreet.sample.toml` for examples. + +- **Clock**: Located at the top of the screen and allows for `strftime`-like format speficiers. +- **Power menu**: Supports `systemd` and a generic linux `shutdown` command. You can configure the order and presence of + power menu actions. + **NOTE:** For configuring other essential features, such as the keyboard layout/mapping, the choice of monitor to use, etc., please check out the configuration options for the wayland compositor that you are using to run ReGreet. For example, if you use Cage, check out the [Cage wiki](https://github.com/cage-kiosk/cage/wiki/Configuration). If you use Sway, check out the [Sway wiki](https://github.com/swaywm/sway/wiki#configuration). @@ -258,22 +266,8 @@ For a general reference on CSS, please refer to the [MDN web docs](https://devel **Tip:** You might want to use [demo mode](#demo-mode) to test out your CSS before making it permanent. -### Changing Reboot/Shut Down Commands -The default reboot and shut down commands use the `reboot` and `poweroff` binaries, which are present on most Linux systems. -However, since the recommended way of using ReGreet is to avoid running it as root, the `reboot`/`poweroff` commands might not work on systems where superuser access is needed to run these commands. -In this case, if there is another command to reboot or shut down the system without superuser access, these commands can be set in the config file under the `[commands]` section. - -For example, to use `loginctl reboot` as the reboot command, use the following config: -```toml -[commands] -reboot = [ "loginctl", "reboot" ] -``` -Here, each command needs to be separated into a list containing the main command, followed by individual arguments. - -These commands can also be specified during compilation using the `REBOOT_CMD` and `POWEROFF_CMD` environment variables. - -### Logging and Caching -The state is are stored in `/var/lib/regreet/state.toml` (configurable during installation). +## Logging and Caching +The cache is are stored in `/var/cache/regreet/cache.toml` (configurable during installation). It contains the last authenticated user and the last used session per user, which are automatically selected on next login. If the greeter is unable to write to this file, then it reverts to the default behaviour. diff --git a/docs/CONFIG.md b/docs/CONFIG.md new file mode 100644 index 0000000..4768d37 --- /dev/null +++ b/docs/CONFIG.md @@ -0,0 +1,133 @@ + +# ReGreet configuration docs + +## Clock + +The clock widget can be customized to specify the +[time display format](https://docs.rs/jiff/0.1.14/jiff/fmt/strtime/index.html#conversion-specifications), +how often to update the clock, overrides to the system timezone and a custom fixed label width so that the non-monospace +font does not cause annoying width changes in the UI. + +```toml +[widget.clock] +# strftime format argument +format = "%a %H:%M" + +# How often to update the text +resolution = "500ms" + +# Override system timezone (IANA Time Zone Database name, aka /etc/zoneinfo path) +# Remove to use the system time zone. +timezone = "America/Chicago" + +# Ask GTK to make the label at least this wide. This helps keeps the parent element layout and width consistent. +# Experiment with different widths, the interpretation of this value is entirely up to GTK. +label_width = 150 +``` + +## Power menu + +The first step in configuring the power menu is to select the backend to use. The backend is specified as part of the +path under `widget.power-menu.`. Please note that only 1 backend can be active at a time. + +For your convenience, most backends allow you to specify an action that is automatically translated into the supported +languages. The correct icon is also selected. Actions that will poweroff your system will ask for confirmation before +they are executed. The actions are listed in the table below. + +| Config value | Confirmation required | +|-----------------|-----------------------| +| poweroff | Yes | +| halt | Yes | +| reboot | Yes | +| reboot-firmware | Yes | +| suspend | No | +| hibernate | No | +| hybrid-sleep | No | + +The convention most backends' configs follow is having a single `actions` key, which is an array of actions. This simple +structure allows you to easily add, remove, and reorder the buttons in the menu without having to verbosely specify all +the attributes like you can do with the [custom](#custom) backend. + +### Systemd + +Machines using Systemd as the init system can specify the following line to use the default configuration. + +```toml +[widget.power-menu.systemd] +``` + +The displayed actions can be changed using the `actions` key. [See above](#power-menu) for more details + +```toml +[widget.power-menu.systemd] +actions = ["suspend", "poweroff", "reboot-firmware"] +``` + +### Unix + +This backend should work on most systems as it does not use commands shipped with any specific init system. Instead, the +Linux `shutdown` command is used. + +```toml +[widget.power-menu.unix] +``` + +> [!NOTE] +> The only actions supported by this backend are poweroff, reboot, and halt. +> +> When other values are specified they are logged as warnings and silently skipped. + +```toml +[widget.power-menu.systemd] +actions = ["poweroff", "reboot", "halt"] +``` + +### Custom + +The custom backend allows you to fully customize the power menu. If none of the other backends suit your needs or +require additional customizations, consider using this backend. + +It is necesary to set the `backend` key to the backend that you are implementing. The backend string can have any value +and is only used as part of the label in the power menu. The actions can be specified in the `commands` key. + +The power menu button (as well as `commands`) has the following fields: + +- `String` Label +- `String` Icon (optional) +- `bool` Should this entry be confirmed? +- `Vec` Command to execute + +The label, icon and confirmation can be inferred by ReGreet automatically if you set the `action` field. + +```toml +[widget.power-menu.custom] +backend = "Custom" # Required value + +[[widget.power-menu.custom.commands]] +action = "poweroff" +command = ["shutdown", "--now"] +# `action` Infers: +# label = "..." ## NOTE: label and action are mutually exclusive +# icon = "..." +# confirm = ... +``` + +Overrides (that are different from the action defaults) can be specified for the icon and the confirm, but not the +label. The logic is that if you are specifying a custom label, you are likely also specifying a custom command that +ReGreet does not know how to interpret into the inferable fields. + +The icon can be hidden altogether by setting it to an empty string. + +```toml +[[widget.power-menu.custom.commands]] +action = "poweroff" +command = ["shutdown", "--now"] +icon = "" # no icon +confirm = false +# Infers: +# label = Poweroff" ## NOTE: label and action are mutually exclusive +``` diff --git a/i18n.toml b/i18n.toml new file mode 100644 index 0000000..9e70cc8 --- /dev/null +++ b/i18n.toml @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2025 max-ishere <47008271+max-ishere@users.noreply.github.com> +# +# SPDX-License-Identifier: GPL-3.0-or-later + +# Docs: https://crates.io/crates/cargo-i18n/ + +fallback_language = "en" + +[fluent] +assets_dir = "i18n" diff --git a/i18n/en/regreet.ftl b/i18n/en/regreet.ftl new file mode 100644 index 0000000..a888692 --- /dev/null +++ b/i18n/en/regreet.ftl @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2025 max-ishere <47008271+max-ishere@users.noreply.github.com> +# +# SPDX-License-Identifier: GPL-3.0-or-later + +dialog-cancel = Cancel + +# Power menu + +power-menu-tooltip = Power menu +power-menu-poweroff = Poweroff +power-menu-halt = Halt +power-menu-reboot = Reboot +power-menu-reboot-firmware = Reboot into firmware +power-menu-suspend = Suspend +power-menu-hibernate = Hibernate +power-menu-hybrid-sleep = Hybrid sleep +power-menu-confirm-dialog-heading = Really { $what }? + +power-menu-backend-systemd = Systemd +power-menu-backend-unix = Unix +power-menu-backend = Backend \ No newline at end of file diff --git a/i18n/uk/regreet.ftl b/i18n/uk/regreet.ftl new file mode 100644 index 0000000..711fbb7 --- /dev/null +++ b/i18n/uk/regreet.ftl @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2025 max-ishere <47008271+max-ishere@users.noreply.github.com> +# +# SPDX-License-Identifier: GPL-3.0-or-later + +dialog-cancel = Відмінити + +# Power menu + +power-menu-tooltip = Керування живленням +power-menu-poweroff = Вимкнути +power-menu-halt = Зупинити +power-menu-reboot = Перезапустити +power-menu-reboot-firmware = Перезапустити до меню загрузки +power-menu-suspend = Сон +power-menu-hibernate = Гибернація +power-menu-hybrid-sleep = Гібрідний сон +power-menu-confirm-dialog-heading = Точно { $what }? + +power-menu-backend-systemd = Systemd +power-menu-backend-unix = Юнікс +power-menu-backend = Реалізація \ No newline at end of file diff --git a/regreet.sample.toml b/regreet.sample.toml index b85f8b0..685e2e1 100644 --- a/regreet.sample.toml +++ b/regreet.sample.toml @@ -12,10 +12,12 @@ path = "/usr/share/backgrounds/greeter.jpg" # NOTE: This is ignored if ReGreet isn't compiled with GTK v4.8 support. fit = "Contain" + # The entries defined in this section will be passed to the session as environment variables when it is started [env] ENV_VARIABLE = "value" + [GTK] # Whether to use the dark theme application_prefer_dark_theme = true @@ -43,7 +45,7 @@ reboot = ["systemctl", "reboot"] poweroff = ["systemctl", "poweroff"] # The command prefix for X11 sessions to start the X server -x11_prefix = [ "startx", "/usr/bin/env" ] +x11_prefix = ["startx", "/usr/bin/env"] [appearance] # The message that initially displays on startup @@ -65,3 +67,14 @@ timezone = "America/Chicago" # Ask GTK to make the label at least this wide. This helps keeps the parent element layout and width consistent. # Experiment with different widths, the interpretation of this value is entirely up to GTK. label_width = 150 + + +# The power menu widget +[widget.power_menu.systemd] # systemd-aware + +# Selects what actions are displayed in the power menu +actions = ["poweroff", "reboot"] + +# You can also use this power menu type: +#[widget.power_menu.unix] +#actions = ["poweroff", "reboot", "halt"] diff --git a/src/config.rs b/src/config.rs index cd05323..7ae2f81 100644 --- a/src/config.rs +++ b/src/config.rs @@ -9,8 +9,9 @@ use std::path::Path; use serde::{Deserialize, Serialize}; -use crate::constants::{GREETING_MSG, POWEROFF_CMD, REBOOT_CMD, X11_CMD_PREFIX}; +use crate::constants::{GREETING_MSG, X11_CMD_PREFIX}; use crate::gui::widget::clock::ClockConfig; +use crate::gui::widget::power_menu::PowerMenuConfig; use crate::tomlutils::load_toml; #[derive(Deserialize, Serialize)] @@ -70,10 +71,6 @@ struct Background { /// Struct for various system commands #[derive(Deserialize, Serialize)] pub struct SystemCommands { - #[serde(default = "default_reboot_command")] - pub reboot: Vec, - #[serde(default = "default_poweroff_command")] - pub poweroff: Vec, #[serde(default = "default_x11_command_prefix")] pub x11_prefix: Vec, } @@ -81,21 +78,11 @@ pub struct SystemCommands { impl Default for SystemCommands { fn default() -> Self { SystemCommands { - reboot: default_reboot_command(), - poweroff: default_poweroff_command(), x11_prefix: default_x11_command_prefix(), } } } -fn default_reboot_command() -> Vec { - shlex::split(REBOOT_CMD).expect("Unable to lex reboot command") -} - -fn default_poweroff_command() -> Vec { - shlex::split(POWEROFF_CMD).expect("Unable to lex poweroff command") -} - fn default_x11_command_prefix() -> Vec { shlex::split(X11_CMD_PREFIX).expect("Unable to lex X11 command prefix") } @@ -130,6 +117,9 @@ pub struct Config { pub struct WidgetConfig { #[serde(default)] pub(crate) clock: ClockConfig, + + #[serde(default)] + pub(crate) power_menu: PowerMenuConfig, } impl Config { @@ -162,3 +152,17 @@ impl Config { self.appearance.greeting_msg.clone() } } + +#[cfg(test)] +mod tests { + use crate::tomlutils::{load_raw_toml, TomlFileResult}; + + use super::*; + + #[test] + fn sample_config_works() -> TomlFileResult<()> { + let _: Config = load_raw_toml("regreet.sample.toml")?; + + Ok(()) + } +} diff --git a/src/constants.rs b/src/constants.rs index abacaef..3bbcd74 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -39,11 +39,6 @@ const LOG_DIR: &str = env_or!("LOG_DIR", concatcp!("/var/log/", GREETER_NAME)); /// Path to the log file pub const LOG_PATH: &str = concatcp!(LOG_DIR, "/log"); -/// Default command for rebooting -pub const REBOOT_CMD: &str = env_or!("REBOOT_CMD", "reboot"); -/// Default command for shutting down -pub const POWEROFF_CMD: &str = env_or!("POWEROFF_CMD", "poweroff"); - /// Default greeting message pub const GREETING_MSG: &str = "Welcome back!"; diff --git a/src/gui/component.rs b/src/gui/component.rs index eb8385e..a3cb161 100644 --- a/src/gui/component.rs +++ b/src/gui/component.rs @@ -22,7 +22,7 @@ use super::model::{Greeter, InputMode, Updates}; use super::templates::Ui; /// Load GTK settings from the greeter config. -fn setup_settings(model: &Greeter, root: >k::ApplicationWindow) { +fn setup_settings(model: &Greeter, root: &adw::ApplicationWindow) { let settings = root.settings(); let config = if let Some(config) = model.config.get_gtk_settings() { config @@ -116,7 +116,7 @@ impl AsyncComponent for Greeter { view! { // The `view!` macro needs a proper widget, not a template, as the root. #[name = "window"] - gtk::ApplicationWindow { + adw::ApplicationWindow { set_visible: true, // Name the UI widget, otherwise the inner children cannot be accessed by name. @@ -131,6 +131,14 @@ impl AsyncComponent for Greeter { model.clock.widget(), }, + #[template_child] + panel_end { + append = model.power_menu.widget() { + set_halign: gtk::Align::End, + set_hexpand: false, + }, + }, + #[template_child] message_label { #[track(model.updates.changed(Updates::message()))] @@ -309,10 +317,6 @@ impl AsyncComponent for Greeter { #[track(model.updates.changed(Updates::error()))] set_label: model.updates.error.as_ref().unwrap_or(&"".to_string()), }, - #[template_child] - reboot_button { connect_clicked => Self::Input::Reboot }, - #[template_child] - poweroff_button { connect_clicked => Self::Input::PowerOff }, } } } @@ -416,8 +420,6 @@ impl AsyncComponent for Greeter { Self::Input::ToggleManualSess => self .updates .set_manual_sess_mode(!self.updates.manual_sess_mode), - Self::Input::Reboot => self.reboot_click_handler(&sender), - Self::Input::PowerOff => self.poweroff_click_handler(&sender), } } diff --git a/src/gui/icons.rs b/src/gui/icons.rs new file mode 100644 index 0000000..45b24e8 --- /dev/null +++ b/src/gui/icons.rs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2025 max-ishere <47008271+max-ishere@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-or-later + +macro_rules! icons { + ($($name:ident = $value:expr),+$(,)?) => { + $(pub const $name: &str = $value;)+ + }; +} + +icons![ + POWER_MENU = self::POWEROFF, + POWEROFF = "system-shutdown-symbolic", + REBOOT = "system-reboot-symbolic", + REBOOT_FIRMWARE = "application-x-firmware-symbolic", + SUSPEND = "system-suspend-symbolic", + HIBERNATE = "system-hibernate-symbolic", +]; diff --git a/src/gui/messages.rs b/src/gui/messages.rs index f5c6d9b..7d766d4 100644 --- a/src/gui/messages.rs +++ b/src/gui/messages.rs @@ -56,8 +56,6 @@ pub enum InputMsg { ToggleManualUser, /// Toggle manual entry of session. ToggleManualSess, - Reboot, - PowerOff, } #[derive(Debug)] diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 1ef2eba..ec13ce1 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -10,7 +10,11 @@ mod model; mod templates; pub(crate) mod widget { pub mod clock; + pub mod power_menu; } +pub mod icons; pub use component::GreeterInit; pub use model::Greeter; + +const GAP: i32 = 15; diff --git a/src/gui/model.rs b/src/gui/model.rs index 258c6da..2a9a3e8 100644 --- a/src/gui/model.rs +++ b/src/gui/model.rs @@ -9,7 +9,6 @@ //! The main logic for the greeter use std::path::Path; -use std::process::Command; use std::sync::Arc; use std::time::Duration; @@ -19,7 +18,7 @@ use relm4::{ gdk::{Display, Monitor}, prelude::*, }, - AsyncComponentSender, Component, Controller, + prelude::*, }; use tokio::{sync::Mutex, time::sleep}; @@ -30,7 +29,7 @@ use crate::sysutil::{SessionInfo, SessionType, SysUtil}; use super::{ messages::{CommandMsg, UserSessInfo}, - widget::clock::Clock, + widget::{clock::Clock, power_menu::PowerMenu}, }; const ERROR_MSG_CLEAR_DELAY: u64 = 5; @@ -96,6 +95,7 @@ pub struct Greeter { pub(super) demo: bool, pub(super) clock: Controller, + pub(super) power_menu: AsyncController, } impl Greeter { @@ -125,6 +125,10 @@ impl Greeter { .launch(config.widget.clock.clone()) .detach(); + let power_menu = PowerMenu::builder() + .launch(config.widget.power_menu.clone()) + .detach(); + Self { greetd_client, sys_util: SysUtil::new(&config).expect("Couldn't read available users and sessions"), @@ -134,6 +138,7 @@ impl Greeter { updates, demo, clock, + power_menu, } } @@ -176,51 +181,6 @@ impl Greeter { self.updates.set_monitor(chosen_monitor); } - /// Run a command and log any errors in a background thread. - fn run_cmd(command: &[String], sender: &AsyncComponentSender) { - let mut process = Command::new(&command[0]); - process.args(command[1..].iter()); - // Run the command and check its output in a separate thread, so as to not block the GUI. - sender.spawn_command(move |_| match process.output() { - Ok(output) => { - if !output.status.success() { - if let Ok(err) = std::str::from_utf8(&output.stderr) { - error!("Failed to launch command: {err}") - } else { - error!("Failed to launch command: {:?}", output.stderr) - } - } - } - Err(err) => error!("Failed to launch command: {err}"), - }); - } - - /// Event handler for clicking the "Reboot" button - /// - /// This reboots the PC. - #[instrument(skip_all)] - pub(super) fn reboot_click_handler(&self, sender: &AsyncComponentSender) { - if self.demo { - info!("demo: skip reboot"); - return; - } - info!("Rebooting"); - Self::run_cmd(&self.config.get_sys_commands().reboot, sender); - } - - /// Event handler for clicking the "Power-Off" button - /// - /// This shuts down the PC. - #[instrument(skip_all)] - pub(super) fn poweroff_click_handler(&self, sender: &AsyncComponentSender) { - if self.demo { - info!("demo: skip shutdown"); - return; - } - info!("Shutting down"); - Self::run_cmd(&self.config.get_sys_commands().poweroff, sender); - } - /// Event handler for clicking the "Cancel" button /// /// This cancels the created session and goes back to the user/session chooser. diff --git a/src/gui/templates.rs b/src/gui/templates.rs index f8f944d..78767d3 100644 --- a/src/gui/templates.rs +++ b/src/gui/templates.rs @@ -8,16 +8,7 @@ use gtk::prelude::*; use relm4::{gtk, RelmWidgetExt, WidgetTemplate}; -/// Button that ends the greeter (eg. Reboot) -#[relm4::widget_template(pub)] -impl WidgetTemplate for EndButton { - view! { - gtk::Button { - set_focusable: true, - add_css_class: "destructive-action", - } - } -} +use crate::gui::GAP; /// Label for an entry/combo box #[relm4::widget_template(pub)] @@ -46,18 +37,18 @@ impl WidgetTemplate for Ui { add_css_class: "background", gtk::Grid { - set_column_spacing: 15, - set_margin_bottom: 15, - set_margin_end: 15, - set_margin_start: 15, - set_margin_top: 15, - set_row_spacing: 15, + set_column_spacing: GAP as u32, + set_margin_bottom: GAP, + set_margin_end: GAP, + set_margin_start: GAP, + set_margin_top: GAP, + set_row_spacing: GAP as u32, set_width_request: 500, /// Widget to display messages to the user #[name = "message_label"] attach[0, 0, 3, 1] = >k::Label { - set_margin_bottom: 15, + set_margin_bottom: GAP, // Format all messages in boldface. #[wrap(Some)] @@ -132,7 +123,7 @@ impl WidgetTemplate for Ui { /// Collection of action buttons (eg. Login) attach[1, 3, 2, 1] = >k::Box { set_halign: gtk::Align::End, - set_spacing: 15, + set_spacing: GAP, /// Button to cancel password entry #[name = "cancel_button"] @@ -153,29 +144,39 @@ impl WidgetTemplate for Ui { }, }, - /// Clock widget - #[name = "clock_frame"] - add_overlay = >k::Frame { - set_halign: gtk::Align::Center, + add_overlay = >k::CenterBox { + set_halign: gtk::Align::Fill, set_valign: gtk::Align::Start, + set_hexpand:true, + + /// Clock widget + #[name = "clock_frame"] + #[wrap(Some)] + set_center_widget = >k::Frame { + add_css_class: "background", + + // Make it fit cleanly onto the top edge of the screen. + inline_css: " + border-top-right-radius: 0px; + border-top-left-radius: 0px; + border-top-width: 0px; + ", + }, - add_css_class: "background", - - // Make it fit cleanly onto the top edge of the screen. - inline_css: " - border-top-right-radius: 0px; - border-top-left-radius: 0px; - border-top-width: 0px; - ", + #[name = "panel_end"] + #[wrap(Some)] + set_end_widget = >k::Box { + set_hexpand: true, + set_halign: gtk::Align::End, + }, }, + /// Collection of widgets appearing at the bottom - add_overlay = >k::Box { - set_orientation: gtk::Orientation::Vertical, + add_overlay = >k::Box::new(gtk::Orientation::Vertical, GAP) { set_halign: gtk::Align::Center, set_valign: gtk::Align::End, - set_margin_bottom: 15, - set_spacing: 15, + set_margin_bottom: GAP, gtk::Frame { /// Notification bar for error messages @@ -198,23 +199,6 @@ impl WidgetTemplate for Ui { }, } }, - - /// Collection of buttons that close the greeter (eg. Reboot) - gtk::Box { - set_halign: gtk::Align::Center, - set_homogeneous: true, - set_spacing: 15, - - /// Button to reboot - #[name = "reboot_button"] - #[template] - EndButton { set_label: "Reboot" }, - - /// Button to power-off - #[name = "poweroff_button"] - #[template] - EndButton { set_label: "Power Off" }, - }, }, } } diff --git a/src/gui/widget/power_menu/custom.rs b/src/gui/widget/power_menu/custom.rs new file mode 100644 index 0000000..611c7d4 --- /dev/null +++ b/src/gui/widget/power_menu/custom.rs @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: 2025 max-ishere <47008271+max-ishere@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-or-later + +//! A fully customizable power menu backend. + +use super::{Action, PowerMenuInit}; + +#[derive(Deserialize, Clone)] + +/// A fully-customizable power menu. You can run arbitrary commands and set labels for them. See [`Command`] for a list +/// of all properties that can be configured +pub struct CustomPowerMenuConfig { + /// A string label in the UI that indicates the backend this menu uses. + /// + /// For example, if you implement the power menu using `runit` commands, set the backend to Runit. + backend: String, + commands: Vec, +} + +/// A set of customizations that can be done to the power menu item. +/// +/// Setting [`Self::title`] to [`Title::Action`] allows ReGreet to infer the translated label, the icon to use, and the +/// need to confirm the action. +/// +/// However, if the power menu item does not fit any of the known to ReGreet [`Action`]s, you can use the +/// [`Title::Label`] variant and optionally specify the [`Self::confirm`] and the [`Self::icon`]. +#[derive(Deserialize, Clone)] +pub struct Command { + /// The title of the action. This field is flattened in the config, so instead of setting `*command*.title.action`, + /// set `*command*.action`. + #[serde(flatten)] + title: Title, + + /// Command to be executed. + #[serde(alias = "cmd")] + command: Vec, + + /// If `true`, a confirmation will be shown before the [`Self::command`] is executed. + /// + /// The [`Option`] is handled according to these rules: + /// + /// If [`Some`], use the value. Otherwise, try to derive the value from the [`Title::Action`], falling back to + /// `false`. Actions that involve a poweroff are inferred to require confirmation. + confirm: Option, + + /// The icon name to set for this action. A list of installed icons can be looked up using the `icon-library` app. + /// + /// If [`None`], the icon is inferred from the value of [`Title::Action`], falling back to no icon. + /// + /// An empty string can be used to disable the icon for this item. + icon: Option, +} + +#[derive(Deserialize, Clone)] +#[serde(rename_all = "snake_case")] +pub enum Title { + /// One of the `i18n`-supported actions + Action(Action), + + /// Arbitrary name string + Label(String), +} + +impl PowerMenuInit for CustomPowerMenuConfig { + fn backend(&self) -> String { + self.backend.clone() + } + fn commands(self) -> Vec { + let len = self.commands.len(); + self.commands.into_iter().fold( + Vec::with_capacity(len), + |mut acc, + Command { + title, + command, + confirm, + icon, + }| { + let icon = icon.unwrap_or(match title { + Title::Action(action) => action.icon().to_owned(), + Title::Label(_) => String::default(), + }); + + let label = match title { + Title::Action(action) => action.fl(), + Title::Label(label) => label, + }; + + acc.push(super::Command { + label, + command, + confirm: confirm.unwrap_or_default(), + icon, + }); + acc + }, + ) + } +} diff --git a/src/gui/widget/power_menu/mod.rs b/src/gui/widget/power_menu/mod.rs new file mode 100644 index 0000000..228c109 --- /dev/null +++ b/src/gui/widget/power_menu/mod.rs @@ -0,0 +1,341 @@ +// SPDX-FileCopyrightText: 2025 max-ishere <47008271+max-ishere@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-or-later + +//! A [serde-configurable][`PowerMenuConfig`] power menu. +//! +//! See submodules for information on how to configure each power menu backend. +//! +//! ## Implementing a custom power menu backend +//! +//! Users of ReGreet can use the [`custom`] backend to specify arbitrary commands you want to run. Although this is not +//! intended, nothing stops you from adding entries unrelated to power management. +//! +//! However, if you want to add support for another init system, for example, it is best to implement a custom backend. +//! Fortunately, this is easy to do. +//! +//! You will need to create a config for this backend. Most likely, only having a single `actions: Vec` field +//! will be sufficient. For forward compatibility, please do not skip creating a `struct {}` to hold the config (exactly +//! the `{}` struct format). Having this struct allows future maintainers to add or rename fields more easily without +//! having to deal with config structure migrations. +//! +//! Make sure to implement sensible defaults for the entire struct. In cases where a default value should be inferred +//! after the config is parsed, use an [`Option`] to detect that the user did not set the value and generate the default +//! in the [`PowerMenuInit`] implementation. +//! +//! Finally, implement a coversion from your custom config struct using the [`PowerMenuInit`] trait. The [`Action`] can +//! be used to infer a lot of information such as the [icon](`Action::icon`) and the [translated label](`Action::fl`). +//! The confirmation requirement logic defaults to checking if the action +//! [involves a poweroff](`Action::is_like_poweroff`). +//! +//! Lastly, add your config type as a variant to the [`PowerMenuConfig`]. + +use custom::CustomPowerMenuConfig; +use relm4::{ + adw::prelude::*, + gtk::glib::markup_escape_text, + prelude::{AsyncComponentParts, *}, +}; +use serde::Deserialize; +use systemd::SystemdPowerMenuConfig; +use tokio::process; +use unix::UnixPowerMenuConfig; + +use crate::{demo, fl, gui::icons, i18n::lowercase_first_char}; + +mod custom; +mod systemd; +mod unix; + +#[derive(Deserialize, Clone)] +#[serde(rename_all = "snake_case")] +pub enum PowerMenuConfig { + /// Systemd-aware widget + Systemd(SystemdPowerMenuConfig), + Unix(UnixPowerMenuConfig), + Custom(CustomPowerMenuConfig), +} + +impl Default for PowerMenuConfig { + fn default() -> Self { + Self::Systemd(Default::default()) + } +} + +pub struct PowerMenu { + state: MenuState, + commands: Vec, +} + +#[derive(Clone)] +pub struct Command { + /// The label of the action. + pub label: String, + + /// Command to be executed. + pub command: Vec, + + /// If `true`, a confirmation will be shown before the [`Self::command`] is executed. + pub confirm: bool, + + /// The icon name to set for this action. A list of installed icons can be looked up using the `icon-library` app. + /// + /// If empty, no icon will be displayed. + pub icon: String, +} + +#[derive(PartialEq, Eq)] +pub enum MenuState { + /// The default state with all buttons shown. + Menu, + + /// The confimation widget for a specific action. + Confirm(usize), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PowerMenuMsg { + /// Sent when the user selected a power menu button. If the action [requires confirmation](`Command::confirm`) + /// a confirmation screen is shown ([`MenuState::Confirm`] state). + Request(usize), + + /// A confirmation of a [`PowerMenuMsg::Request`] was cancelled. Has no effect if no confirmation is requested. + Cancel, + + /// Confirms an action that was requested in [`PowerMenuMsg::Request`]. Has no effect if no confirmation is requested. + Confirm, +} + +#[relm4::component(pub, async)] +impl AsyncComponent for PowerMenu { + type Init = PowerMenuConfig; + type Input = PowerMenuMsg; + type Output = (); + type CommandOutput = (); + + view! { + gtk::MenuButton { + set_icon_name: icons::POWER_MENU, + set_tooltip: &fl!("power-menu-tooltip"), + + #[wrap(Some)] + set_popover = >k::Popover { + gtk::Box { + #[name = "menu"] + #[transition(Crossfade)] + match model.state { + MenuState::Menu => &adw::PreferencesGroup { + set_title: &markup_escape_text(&fl!("power-menu-tooltip")), + set_description: Some(&format!("{}: {backend}", markup_escape_text(&fl!("power-menu-backend")))), + + #[iterate] + add: &action_buttons(&model.commands, sender.clone()), + }, + + MenuState::Confirm(index) => &adw::PreferencesGroup { + #[watch] + set_title: &markup_escape_text(&fl!("power-menu-confirm-dialog-heading", + what = lowercase_first_char(&model.commands[index].label)) + ), + + set_separate_rows: true, + + adw::ButtonRow { + set_title: &fl!("dialog-cancel"), + + connect_activated => PowerMenuMsg::Cancel, + }, + + adw::ButtonRow { + #[watch] + set_title: &model.commands[index].label, + add_css_class: "destructive-action", + + connect_activated => PowerMenuMsg::Confirm, + } + } + } + } + }, + } + } + + async fn init( + init: Self::Init, + root: Self::Root, + sender: AsyncComponentSender, + ) -> AsyncComponentParts { + let (backend, commands) = match init { + Self::Init::Systemd(systemd_config) => { + (systemd_config.backend(), systemd_config.commands()) + } + + Self::Init::Unix(unix_power_menu_config) => ( + unix_power_menu_config.backend(), + unix_power_menu_config.commands(), + ), + + Self::Init::Custom(custom_power_menu_config) => ( + custom_power_menu_config.backend(), + custom_power_menu_config.commands(), + ), + }; + + let model = Self { + commands, + state: MenuState::Menu, + }; + + let widgets = view_output!(); + + widgets.menu.set_vhomogeneous(false); + widgets.menu.set_hhomogeneous(false); + + AsyncComponentParts { model, widgets } + } + + async fn update( + &mut self, + message: Self::Input, + sender: AsyncComponentSender, + _: &Self::Root, + ) { + use PowerMenuMsg as M; + let index = match message { + M::Request(index) => index, + + M::Confirm => { + let MenuState::Confirm(what) = self.state else { + return; + }; + what + } + + M::Cancel => { + self.state = MenuState::Menu; + return; + } + }; + + let Command { + confirm, + label, + command, + .. + } = &self.commands[index]; + + let require_confirm = self.state == MenuState::Menu && *confirm; + + if require_confirm { + self.state = MenuState::Confirm(index); + return; + } + + if demo() { + info!("Demo mode: not doing {label}"); + self.state = MenuState::Menu; + + return; + } + + let command = process::Command::new(&command[0]) + .args(&command[1..]) + .status(); + + let label = label.clone(); + sender.oneshot_command(async move { + let Err(why) = command.await else { return }; + debug!("Failed to {label}: {why}"); + }); + } +} + +fn action_buttons( + commands: &[Command], + sender: AsyncComponentSender, +) -> Vec { + commands.iter().enumerate().fold( + Vec::with_capacity(commands.len()), + |mut acc, (index, command)| { + let button = adw::ButtonRow::new(); + button.set_title(&command.label); + + if !command.icon.is_empty() { + button.set_start_icon_name(Some(&command.icon)); + } + + let sender = sender.clone(); + button.connect_activated(move |_| sender.input(PowerMenuMsg::Request(index))); + + acc.push(button); + acc + }, + ) +} + +impl Command { + pub fn new(label: String, icon: String, confirm: bool, command: Vec) -> Self { + Self { + label, + command, + confirm, + icon, + } + } +} + +#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[serde(rename_all = "kebab-case")] +pub enum Action { + Poweroff, + Halt, + Reboot, + RebootFirmware, + Suspend, + Hibernate, + HybridSleep, +} + +impl Action { + /// Returns the icon name for this action + pub const fn icon(&self) -> &'static str { + match self { + Self::Poweroff => crate::gui::icons::POWEROFF, + Self::Halt => crate::gui::icons::POWEROFF, + Self::Reboot => crate::gui::icons::REBOOT, + Self::RebootFirmware => crate::gui::icons::REBOOT_FIRMWARE, + Self::Suspend => crate::gui::icons::SUSPEND, + Self::Hibernate => crate::gui::icons::HIBERNATE, + Self::HybridSleep => crate::gui::icons::HIBERNATE, + } + } + + /// Returns the [`crate::fl!`] for the variant using this format: `fl!("power-menu-{kebab-case}")` + pub fn fl(&self) -> String { + use Action as A; + match self { + A::Poweroff => fl!("power-menu-poweroff"), + A::Halt => fl!("power-menu-halt"), + A::Reboot => fl!("power-menu-reboot"), + A::RebootFirmware => fl!("power-menu-reboot-firmware"), + A::Suspend => fl!("power-menu-suspend"), + A::Hibernate => fl!("power-menu-hibernate"), + A::HybridSleep => fl!("power-menu-hybrid-sleep"), + } + } + + /// Returns `true` if executing this action would power off the system (meaning reboot counts). Putting the system to + /// sleep does not count as it can be easily undone by waking up the system. + pub const fn is_like_poweroff(&self) -> bool { + matches!( + self, + Self::Poweroff | Self::Halt | Self::Reboot | Self::RebootFirmware + ) + } +} + +/// A convenience trait that converts any power menu config into a set of information that the generic UI can display. +trait PowerMenuInit { + fn backend(&self) -> String; + fn commands(self) -> Vec; +} diff --git a/src/gui/widget/power_menu/systemd.rs b/src/gui/widget/power_menu/systemd.rs new file mode 100644 index 0000000..e7a527b --- /dev/null +++ b/src/gui/widget/power_menu/systemd.rs @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: 2025 max-ishere <47008271+max-ishere@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-or-later + +//! The Systemd power menu backend. +//! +//! See supported actions as variants of [`Action`]. + +use std::collections::HashSet; + +use crate::fl; + +use super::{Action, Command, PowerMenuInit}; + +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "snake_case")] +pub struct SystemdPowerMenuConfig { + /// The list of actions to show. Order is preserved. + /// + /// The first unique occurance is used, with duplicates discarded. + /// E.g. `["poweroff", "reboot", "poweroff"]` Results in this order of widgets: Poweroff, Reboot. + /// + /// The button labels, icons and `systemctl` commands are automatically selected based on the action you specify. + #[serde(default = "default_actions")] + pub actions: Vec, +} + +impl Default for SystemdPowerMenuConfig { + fn default() -> Self { + Self { + actions: default_actions(), + } + } +} + +/// Sensible default set of actions that most users would find sufficient. +fn default_actions() -> Vec { + [Action::Poweroff, Action::Reboot, Action::Suspend].into() +} + +impl PowerMenuInit for SystemdPowerMenuConfig { + fn backend(&self) -> String { + fl!("power-menu-backend-systemd") + } + + fn commands(self) -> Vec { + let mut mentioned = HashSet::new(); + + let len = self.actions.len(); + + self.actions + .into_iter() + .fold(Vec::with_capacity(len), |mut acc, action| { + if !mentioned.insert(action) { + return acc; + } + + let command = match action { + Action::Poweroff => vec!["systemctl".to_string(), "poweroff".to_string()], + Action::Halt => vec!["systemctl".to_string(), "halt".to_string()], + Action::Reboot => vec!["systemctl".to_string(), "reboot".to_string()], + Action::RebootFirmware => vec![ + "systemctl".to_string(), + "reboot".to_string(), + "--firmware-setup".to_string(), + ], + Action::Suspend => vec!["systemctl".to_string(), "suspend".to_string()], + Action::Hibernate => vec!["systemctl".to_string(), "hibernate".to_string()], + Action::HybridSleep => { + vec!["systemctl".to_string(), "hybrid-sleep".to_string()] + } + }; + + acc.push(Command::new( + action.fl(), + action.icon().to_owned(), + action.is_like_poweroff(), + command, + )); + acc + }) + } +} diff --git a/src/gui/widget/power_menu/unix.rs b/src/gui/widget/power_menu/unix.rs new file mode 100644 index 0000000..c1b819e --- /dev/null +++ b/src/gui/widget/power_menu/unix.rs @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2025 max-ishere <47008271+max-ishere@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-or-later + +//! A power menu backend that uses the generic `man 8 shutdown` linux command. + +use std::collections::HashSet; + +use crate::fl; + +use super::{Action, Command, PowerMenuInit}; + +#[derive(Deserialize, Clone)] +pub struct UnixPowerMenuConfig { + /// The list of actions to show. Order is preserved. + /// + /// The first unique occurance is used, with duplicates discarded. + /// E.g. `["poweroff", "reboot", "poweroff"]` Results in this order of widgets: Poweroff, Reboot. + /// + /// The button labels, icons and `systemctl` commands are automatically selected based on the action you specify. + /// + /// Please note that only `poweroff`, `reboot`, and `halt` are supported by the unix `shutdown` command. Other + /// actions are silently filtered out. + #[serde(default = "default_actions")] + actions: Vec, +} + +pub fn default_actions() -> Vec { + [Action::Poweroff, Action::Reboot].into() +} + +impl PowerMenuInit for UnixPowerMenuConfig { + fn backend(&self) -> String { + fl!("power-menu-backend-unix") + } + + fn commands(self) -> Vec { + let mut mentioned = HashSet::new(); + + let len = self.actions.len(); + self.actions + .into_iter() + .fold(Vec::with_capacity(len), |mut acc, action| { + if !mentioned.insert(action) { + return acc; + } + + let command = match action { + Action::Poweroff => { + vec![ + "shutdown".to_string(), + "--poweroff".to_string(), + "now".to_string(), + ] + } + Action::Reboot => vec![ + "shutdown".to_string(), + "--reboot".to_string(), + "now".to_string(), + ], + Action::Halt => vec![ + "shutdown".to_string(), + "--halt".to_string(), + "now".to_string(), + ], + unsupported => { + info!("{unsupported:?} is not supported in the Unix power menu backend."); + return acc; + } + }; + + acc.push(Command::new( + action.fl(), + action.icon().to_owned(), + action.is_like_poweroff(), + command, + )); + acc + }) + } +} diff --git a/src/i18n.rs b/src/i18n.rs new file mode 100644 index 0000000..7238921 --- /dev/null +++ b/src/i18n.rs @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2025 max-ishere <47008271+max-ishere@users.noreply.github.com> +// +// SPDX-License-Identifier: GPL-3.0-or-later + +//! # Internationalization +//! +//! Firstly Initialize the language selection with [`init`]. Then use the [`fl!`] macro to request a string by id. +//! +//! [`fl!`]: crate::fl + +use std::sync::LazyLock; + +use i18n_embed::{ + fluent::{fluent_language_loader, FluentLanguageLoader}, + DefaultLocalizer, LanguageLoader as _, Localizer, +}; +use rust_embed::RustEmbed; +use unic_langid::LanguageIdentifier; + +#[derive(RustEmbed)] +#[folder = "i18n"] +struct Localizations; + +/// Applies the requested language(s) to be used when calling the `fl!()` macro. +pub fn init(requested_languages: &[LanguageIdentifier]) { + if let Err(err) = localizer().select(requested_languages) { + eprintln!("error while loading fluent localizations: {err}"); + } +} + +/// Get the `Localizer` to be used for localizing this library. +#[must_use] +fn localizer() -> Box { + Box::from(DefaultLocalizer::new(&*LANGUAGE_LOADER, &Localizations)) +} + +pub static LANGUAGE_LOADER: LazyLock = LazyLock::new(|| { + let loader: FluentLanguageLoader = fluent_language_loader!(); + + loader + .load_fallback_language(&Localizations) + .expect("Error while loading fallback language"); + + loader +}); + +/// Request a localized string by ID. +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id) + }}; + + ($message_id:literal, $($args:expr),*) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id, $($args), *) + }}; +} + +/// Lowercase the first letter of the string. +pub(crate) fn lowercase_first_char(string: &str) -> String { + let mut chars = string.chars(); + let Some(uppercase) = chars.next() else { + return "".into(); + }; + + let lowercase = uppercase.to_lowercase().to_string(); + lowercase + chars.collect::().as_str() +} diff --git a/src/main.rs b/src/main.rs index 30028a4..b399710 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,15 +7,18 @@ mod client; mod config; mod constants; mod gui; +mod i18n; mod sysutil; mod tomlutils; use std::fs::{create_dir_all, OpenOptions}; use std::io::{Result as IoResult, Write}; use std::path::{Path, PathBuf}; +use std::sync::OnceLock; use clap::{Parser, ValueEnum}; use file_rotate::{compression::Compression, suffix::AppendCount, ContentLimit, FileRotate}; +use i18n_embed::DesktopLanguageRequester; use tracing::subscriber::set_global_default; use tracing_appender::{non_blocking, non_blocking::WorkerGuard}; use tracing_subscriber::{ @@ -31,6 +34,8 @@ extern crate tracing; extern crate lazy_static; #[macro_use] extern crate const_format; +#[macro_use(Deserialize)] +extern crate serde; #[cfg(test)] #[macro_use] @@ -39,6 +44,13 @@ extern crate test_case; const MAX_LOG_FILES: usize = 3; const MAX_LOG_SIZE: usize = 1024 * 1024; +static DEMO: OnceLock = OnceLock::new(); + +/// Get the demo mode status +fn demo() -> bool { + *DEMO.get().unwrap_or(&false) +} + #[derive(Clone, Debug, ValueEnum)] enum LogLevel { Off, @@ -78,7 +90,11 @@ struct Args { } fn main() { + i18n::init(&DesktopLanguageRequester::requested_languages()); + let args = Args::parse(); + DEMO.get_or_init(|| args.demo); + // Keep the guard alive till the end of the function, since logging depends on this. let _guard = init_logging(&args.logs, &args.log_level, args.verbose); diff --git a/src/tomlutils.rs b/src/tomlutils.rs index e8f1e06..8889fa2 100644 --- a/src/tomlutils.rs +++ b/src/tomlutils.rs @@ -17,16 +17,16 @@ pub enum TomlFileError { IO(#[from] std::io::Error), #[error("Error decoding UTF-8")] Utf8(#[from] std::str::Utf8Error), - #[error("Error decoding TOML file contents")] + #[error("Error decoding TOML file contents: {0}")] TomlDecode(#[from] toml::de::Error), - #[error("Error encoding into TOML")] + #[error("Error encoding into TOML: {0}")] TomlEncode(#[from] toml::ser::Error), } pub type TomlFileResult = Result; /// Load the TOML file from disk without any checks. -fn load_raw_toml(path: &Path) -> TomlFileResult { +pub(crate) fn load_raw_toml>(path: P) -> TomlFileResult { Ok(toml::from_str(std::str::from_utf8( read(path)?.as_slice(), )?)?)