diff --git a/Cargo.lock b/Cargo.lock index 21d97db..e5fef8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -242,6 +242,17 @@ dependencies = [ "libc", ] +[[package]] +name = "annotate-snippets" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f211a51805bc641f3ad5b7664c77d2547af685cc33b4cd8d31964027a46f13f1" +dependencies = [ + "anstyle", + "memchr", + "unicode-width 0.2.2", +] + [[package]] name = "anstream" version = "1.0.0" @@ -278,7 +289,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -289,7 +300,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -339,6 +350,12 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + [[package]] name = "arrayref" version = "0.3.9" @@ -544,6 +561,16 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atomic-write-file" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae67d5c03ee972c101a1d8db3ddbf8a9c819ad1ea9d6fd9cd385605547b60edb" +dependencies = [ + "nix", + "rand 0.10.2", +] + [[package]] name = "atomicwrites" version = "0.4.2" @@ -662,6 +689,12 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "az" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be5eb007b7cacc6c660343e96f650fedf4b5a77512399eb952ca6642cf8d13f7" + [[package]] name = "backtrace" version = "0.3.76" @@ -677,12 +710,24 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "base62" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3a647dce9631f152a99ec16cfc09267ae55fe280d5dc288964027d458076323" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "basic-toml" version = "0.1.10" @@ -692,6 +737,15 @@ dependencies = [ "serde", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bindgen" version = "0.71.1" @@ -1083,6 +1137,17 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" +[[package]] +name = "chacha20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + [[package]] name = "chrono" version = "0.4.45" @@ -1232,7 +1297,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ "serde", "termcolor", - "unicode-width 0.1.14", + "unicode-width 0.2.2", ] [[package]] @@ -1305,6 +1370,15 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -1366,6 +1440,12 @@ dependencies = [ "libc", ] +[[package]] +name = "core_detect" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" + [[package]] name = "core_maths" version = "0.1.1" @@ -1585,6 +1665,8 @@ dependencies = [ "nmrs", "notify", "num-traits", + "openlogi-core", + "openlogi-ipc", "pwhash", "regex", "ron 0.12.2", @@ -1599,6 +1681,7 @@ dependencies = [ "static_init", "sunrise", "sysinfo", + "tarpc", "timedate-zbus", "tokio", "tracing", @@ -2183,7 +2266,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2229,6 +2312,12 @@ dependencies = [ "smithay-clipboard", ] +[[package]] +name = "doctest-file" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2db04e74f0a9a93103b50e90b96024c9b2bdca8bce6a632ec71b88736d3d359" + [[package]] name = "document-features" version = "0.2.12" @@ -2260,18 +2349,74 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +[[package]] +name = "educe" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4bd92664bf78c4d3dba9b7cdafce6fa15b13ed3ed16175218196942e99168a8" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "either" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" +[[package]] +name = "encoding_rs" +version = "0.8.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5ef0006ac9ab233c38522f5ae99cae3625151de8f706cacee1cba4b8e2832a" +dependencies = [ + "cfg-if", + "core_detect", + "multiversion", + "multiversion_no_op", + "rustversion", + "scopeguard", + "simdutf8", +] + +[[package]] +name = "encoding_rs_io" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba3fe847045ecff794b9c138293a80db914678c453ad63fbf0c6a9eb6e00b22" +dependencies = [ + "encoding_rs", +] + [[package]] name = "endi" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" +[[package]] +name = "enum-ordinalize" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89dd01549b09589510cf0647475075d12071456586d70f5c75c98ae2a5537677" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65863d15a4ce2888bd2f0f543cc963d3879c3a022c8ee43f6141d479a3ac815" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + [[package]] name = "enumflags2" version = "0.7.12" @@ -2326,7 +2471,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2345,6 +2490,16 @@ dependencies = [ "svg_fmt", ] +[[package]] +name = "etcetera" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96" +dependencies = [ + "cfg-if", + "windows-sys 0.61.2", +] + [[package]] name = "euclid" version = "0.22.14" @@ -2463,7 +2618,7 @@ checksum = "79c3c892f121fff406e5dd6b28c1b30096b95111c30701a899d4f2b18da6d1bd" dependencies = [ "displaydoc", "smallvec", - "writeable", + "writeable 0.6.4", ] [[package]] @@ -2513,7 +2668,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01203cb8918f5711e73891b347816d932046f95f54207710bda99beaeb423bf4" dependencies = [ - "fluent-langneg", + "fluent-langneg 0.13.1", "fluent-syntax", "intl-memoizer", "intl_pluralrules", @@ -2532,6 +2687,15 @@ dependencies = [ "unic-langid", ] +[[package]] +name = "fluent-langneg" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d5439a5e4ef14dc761bfe1d6ecbc4ba6fd698814ee65e7e623fa85aa9348dcc" +dependencies = [ + "icu_locid", +] + [[package]] name = "fluent-syntax" version = "0.12.0" @@ -2830,6 +2994,7 @@ dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", + "rand_core 0.10.1", ] [[package]] @@ -2891,6 +3056,30 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" +[[package]] +name = "globset" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + [[package]] name = "glow" version = "0.16.0" @@ -2946,6 +3135,16 @@ dependencies = [ "bitflags 2.13.1", ] +[[package]] +name = "granit-parser" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ec0d45986cd51c847c75c5b69a00852c4fc84d0e5e79f041173f73437d0cdf" +dependencies = [ + "arraydeque", + "smallvec", +] + [[package]] name = "grid" version = "1.0.1" @@ -3117,6 +3316,12 @@ dependencies = [ "zbus", ] +[[package]] +name = "humantime" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15" + [[package]] name = "hybrid-array" version = "0.4.14" @@ -3148,7 +3353,7 @@ checksum = "a217bbb075dcaefb292efa78897fc0678245ca67f265d12c351e42268fcb0305" dependencies = [ "arc-swap", "fluent", - "fluent-langneg", + "fluent-langneg 0.13.1", "fluent-syntax", "i18n-embed-impl", "intl-memoizer", @@ -3484,7 +3689,7 @@ dependencies = [ "icu_provider", "ixdtf", "serde", - "tinystr", + "tinystr 0.8.4", "zerovec", ] @@ -3506,7 +3711,7 @@ dependencies = [ "icu_properties", "icu_provider", "potential_utf", - "writeable", + "writeable 0.6.4", "zerovec", ] @@ -3574,8 +3779,8 @@ dependencies = [ "icu_provider", "icu_time", "potential_utf", - "tinystr", - "writeable", + "tinystr 0.8.4", + "writeable 0.6.4", "zerovec", ] @@ -3600,7 +3805,7 @@ dependencies = [ "icu_plurals", "icu_provider", "serde", - "writeable", + "writeable 0.6.4", "zerovec", ] @@ -3632,14 +3837,14 @@ dependencies = [ "icu_plurals", "icu_properties", "icu_provider", - "litemap", + "litemap 0.8.3", "num-bigint", "num-rational", "num-traits", "potential_utf", "smallvec", - "tinystr", - "writeable", + "tinystr 0.8.4", + "writeable 0.6.4", "zerotrie", "zerovec", ] @@ -3661,7 +3866,7 @@ dependencies = [ "icu_provider", "regex-automata", "serde", - "writeable", + "writeable 0.6.4", "zerovec", ] @@ -3682,7 +3887,7 @@ dependencies = [ "icu_locale_data", "icu_locale_fallback", "icu_provider", - "tinystr", + "tinystr 0.8.4", "zerovec", ] @@ -3693,10 +3898,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" dependencies = [ "displaydoc", - "litemap", + "litemap 0.8.3", "serde", - "tinystr", - "writeable", + "tinystr 0.8.4", + "writeable 0.6.4", "zerovec", ] @@ -3716,7 +3921,7 @@ dependencies = [ "icu_locale_fallback_data", "icu_provider", "potential_utf", - "tinystr", + "tinystr 0.8.4", "zerovec", ] @@ -3726,6 +3931,18 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "decf2a22ec8fa68f1a0c1129a3f8583f8f8bc24e8b9ccbe98ead99f62a4dc3a8" +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap 0.7.5", + "tinystr 0.7.6", + "writeable 0.5.5", +] + [[package]] name = "icu_normalizer" version = "2.3.0" @@ -3758,7 +3975,7 @@ dependencies = [ "displaydoc", "either", "serde", - "writeable", + "writeable 0.6.4", "yoke", "zerovec", ] @@ -3814,7 +4031,7 @@ dependencies = [ "icu_locale_core", "serde", "stable_deref_trait", - "writeable", + "writeable 0.6.4", "yoke", "zerofrom", "zerotrie", @@ -3895,6 +4112,22 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "ignore" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b69833ed729dc5aa7d19541d96d6cf8e9137194207a04916d658e43168402f" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "image" version = "0.25.10" @@ -4045,6 +4278,21 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "interprocess" +version = "2.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb02ffbf25e8ef0f4a95bff054eef75f6d1bf16919474cf2caa63b21997478a" +dependencies = [ + "doctest-file", + "futures-core", + "libc", + "recvmsg", + "tokio", + "widestring", + "windows-sys 0.61.2", +] + [[package]] name = "intl-memoizer" version = "0.5.3" @@ -4087,6 +4335,15 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -4476,13 +4733,34 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "kinded" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21677048d7aa2c4c73e7edac4a9d5aee810a3987ccef2762c8449ffd0b92f8d2" +dependencies = [ + "kinded_macros", +] + +[[package]] +name = "kinded_macros" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eaa04e7e45e39b21765756cc9544a4eb4a7f41abefbe77e6256a1649789551d" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "known-folders" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1886916523694cd6ea3d175f03a1e5010699a2a4cc13696d83d7bea1d80638" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4675,6 +4953,12 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + [[package]] name = "litemap" version = "0.8.3" @@ -4973,6 +5257,33 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "multiversion" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ca4bea16ffc3f443cf7d866912118196bfef4c6a1556ca00f9f9b00bb43f7c" +dependencies = [ + "multiversion-macros", +] + +[[package]] +name = "multiversion-macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d416831a7317ef4b08bee00b69cbbb9c8763da7959a7026244d6266869f9c83" +dependencies = [ + "proc-macro2", + "quote", + "rustversion", + "syn 3.0.3", +] + +[[package]] +name = "multiversion_no_op" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d" + [[package]] name = "mutate_once" version = "0.1.2" @@ -5041,6 +5352,18 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags 2.13.1", + "cfg-if", + "cfg_aliases 0.2.2", + "libc", +] + [[package]] name = "nmrs" version = "3.5.2" @@ -5048,7 +5371,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa84a0ad51eda83c8817099a0b613847599c712d049e89d892cca53ed6c4a337" dependencies = [ "async-trait", - "base64", + "base64 0.22.1", "bitflags 2.13.1", "futures", "futures-timer", @@ -5070,6 +5393,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + [[package]] name = "nom" version = "7.1.3" @@ -5095,6 +5424,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" +[[package]] +name = "normpath" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9985ef7269fa99f3b12437bb698381da2428743ab90f20393f399fa14cab21a" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "notify" version = "8.2.0" @@ -5137,7 +5475,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5253,6 +5591,30 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "nutype" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ae01f500e59a27e8c868297e15e268105d30ae06dee1ca194e025be29dee2a" +dependencies = [ + "nutype_macros", +] + +[[package]] +name = "nutype_macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48a6a6ddc63e6d0cd6b77af9aabae24601b39ad543d29bff88c4761c1fc34700" +dependencies = [ + "cfg-if", + "kinded", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.119", + "urlencoding", +] + [[package]] name = "objc" version = "0.2.7" @@ -5498,6 +5860,63 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openlogi-core" +version = "0.8.3" +dependencies = [ + "atomic-write-file", + "az", + "etcetera", + "fluent-langneg 0.14.2", + "num_enum", + "nutype", + "openlogi-device-registry", + "plist", + "rust-i18n", + "serde", + "strum", + "sys-locale", + "thiserror 2.0.20", + "toml 0.8.23", + "toml_edit 0.22.27", + "tracing", +] + +[[package]] +name = "openlogi-device-registry" +version = "0.8.3" + +[[package]] +name = "openlogi-ipc" +version = "0.8.3" +dependencies = [ + "interprocess", + "openlogi-core", + "serde", + "succession", + "tarpc", + "thiserror 2.0.20", +] + +[[package]] +name = "opentelemetry" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0142c63252a9e054e68a4c61a5778f7b14f576274d593f8ce883d191a099682" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "pin-project-lite", + "thiserror 2.0.20", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c913ac17a6c451661ee255f4625d143e51647ae78ebd969b75e41c4442f4fe47" + [[package]] name = "option-ext" version = "0.2.0" @@ -5756,6 +6175,19 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "plist" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896bade328c13f7042a297ea5ac5b0951f6cf989dea5f32c2fd98da398195cb" +dependencies = [ + "base64 0.23.1", + "indexmap 2.14.0", + "quick-xml 0.42.0", + "serde", + "time", +] + [[package]] name = "png" version = "0.17.16" @@ -5824,7 +6256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" dependencies = [ "serde_core", - "writeable", + "writeable 0.6.4", "zerovec", ] @@ -5865,7 +6297,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit", + "toml_edit 0.25.13+spec-1.1.0", ] [[package]] @@ -6005,6 +6437,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b1177fdf999d2321d3fb46ff47159d9c1fb9ad66a4879f8c50a0b504615e9b" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.47" @@ -6047,6 +6488,17 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -6085,6 +6537,12 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "range-alloc" version = "0.1.5" @@ -6210,6 +6668,12 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" +[[package]] +name = "recvmsg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" + [[package]] name = "redox_event" version = "0.4.8" @@ -6360,7 +6824,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db09040cc89e461f1a265139777a2bde7f8d8c67c4936f700c63ce3e2904d468" dependencies = [ - "base64", + "base64 0.22.1", "bitflags 2.13.1", "serde", "serde_derive", @@ -6422,6 +6886,53 @@ dependencies = [ "walkdir", ] +[[package]] +name = "rust-i18n" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c43fd69c20da13756643a5bf51ae799965465c78e05c57a1242a736aaa56527" +dependencies = [ + "globwalk", + "regex", + "rust-i18n-macro", + "rust-i18n-support", + "smallvec", +] + +[[package]] +name = "rust-i18n-macro" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "086ec8a9eaa6afda33919b9bc1661c1f4e017c40e99bbd33ca98f6e1cb6ca91f" +dependencies = [ + "glob", + "proc-macro2", + "quote", + "rust-i18n-support", + "serde", + "serde_json", + "syn 2.0.119", +] + +[[package]] +name = "rust-i18n-support" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac02dcb9a01ec145d0a5534ea9554a966050ca250b346bb2a9ab0fcdafab5979" +dependencies = [ + "arc-swap", + "base62", + "globwalk", + "itertools 0.11.0", + "normpath", + "serde", + "serde-saphyr", + "serde_json", + "siphasher", + "toml 0.8.23", + "triomphe", +] + [[package]] name = "rustc-demangle" version = "0.1.28" @@ -6472,7 +6983,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6614,6 +7125,23 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-saphyr" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afb591f9cdb6223c88ba39269aff895620c7f0716dc42b705b5733d5c7c0823" +dependencies = [ + "annotate-snippets", + "base64 0.22.1", + "encoding_rs_io", + "granit-parser", + "nohash-hasher", + "num-traits", + "serde_core", + "smallvec", + "zmij", +] + [[package]] name = "serde-xml-rs" version = "0.6.0" @@ -6671,6 +7199,15 @@ dependencies = [ "syn 3.0.3", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "serde_spanned" version = "1.1.1" @@ -6686,7 +7223,7 @@ version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee78f1fbe43ac4a0e47aadb3dbd357b69eb0d3793e948624cd03dd2750ab1c0a" dependencies = [ - "base64", + "base64 0.22.1", "bs58", "chrono", "hex", @@ -6942,7 +7479,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -7034,12 +7571,42 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "subtle" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +[[package]] +name = "succession" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebea0f5590b6dde1056c1ba7e6045d5abce2f69da6a3f438d05b91d246d5bd53" +dependencies = [ + "serde", +] + [[package]] name = "sunrise" version = "3.0.0" @@ -7174,6 +7741,41 @@ version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" +[[package]] +name = "tarpc" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e074d21429aa2864540a8452209323841116b3a5ac1ee7b29b2bbe38c12655b" +dependencies = [ + "fnv", + "futures", + "humantime", + "opentelemetry", + "opentelemetry-semantic-conventions", + "pin-project", + "rand 0.10.2", + "serde", + "static_assertions", + "tarpc-plugins", + "thiserror 2.0.20", + "tokio", + "tokio-serde", + "tokio-util", + "tracing", + "tracing-opentelemetry", +] + +[[package]] +name = "tarpc-plugins" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c1932595af3b23fc8635cc23c28c54fb658622e261ad7f8f026da70e69f350" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + [[package]] name = "temp-dir" version = "0.1.16" @@ -7187,10 +7789,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -7340,6 +7942,15 @@ dependencies = [ "strict-num", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", +] + [[package]] name = "tinystr" version = "0.8.4" @@ -7394,6 +8005,21 @@ dependencies = [ "syn 3.0.3", ] +[[package]] +name = "tokio-serde" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf600e7036b17782571dd44fa0a5cea3c82f60db5137f774a325a76a0d6852b" +dependencies = [ + "bincode", + "bytes", + "educe", + "futures-core", + "futures-sink", + "pin-project", + "serde", +] + [[package]] name = "tokio-stream" version = "0.1.19" @@ -7417,6 +8043,7 @@ dependencies = [ "futures-sink", "libc", "pin-project-lite", + "slab", "tokio", ] @@ -7429,6 +8056,18 @@ dependencies = [ "serde", ] +[[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 = "1.1.4+spec-1.1.0" @@ -7437,13 +8076,22 @@ checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" dependencies = [ "indexmap 2.14.0", "serde_core", - "serde_spanned", - "toml_datetime", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", "winnow 1.0.4", ] +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + [[package]] name = "toml_datetime" version = "1.1.1+spec-1.1.0" @@ -7453,6 +8101,20 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap 2.14.0", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_write", + "winnow 0.7.15", +] + [[package]] name = "toml_edit" version = "0.25.13+spec-1.1.0" @@ -7460,7 +8122,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" dependencies = [ "indexmap 2.14.0", - "toml_datetime", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "winnow 1.0.4", ] @@ -7474,6 +8136,12 @@ dependencies = [ "winnow 1.0.4", ] +[[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.1.2+spec-1.1.0" @@ -7534,6 +8202,20 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-opentelemetry" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adbc64cba7137545b8044cb1fe9814f7aacf3c6b5f9b45be8bb5db538befdb26" +dependencies = [ + "js-sys", + "opentelemetry", + "tracing", + "tracing-core", + "tracing-subscriber", + "web-time", +] + [[package]] name = "tracing-subscriber" version = "0.3.23" @@ -7552,6 +8234,17 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "triomphe" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b40688ea6389c8171614b25491f71d4a27946e0c7ce2da1c6de27e25abf1a0ae" +dependencies = [ + "arc-swap", + "serde", + "stable_deref_trait", +] + [[package]] name = "ttf-parser" version = "0.25.1" @@ -7602,7 +8295,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -7630,7 +8323,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658" dependencies = [ "serde", - "tinystr", + "tinystr 0.8.4", ] [[package]] @@ -7740,7 +8433,7 @@ version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef" dependencies = [ - "base64", + "base64 0.22.1", "data-url", "flate2", "fontdb", @@ -8250,6 +8943,12 @@ dependencies = [ "libc", ] +[[package]] +name = "widestring" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" + [[package]] name = "winapi" version = "0.3.9" @@ -8272,7 +8971,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -8913,6 +9612,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "1.0.4" @@ -8934,6 +9642,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "writeable" version = "0.6.4" diff --git a/cosmic-settings/Cargo.toml b/cosmic-settings/Cargo.toml index 491dea7..35dfdf5 100644 --- a/cosmic-settings/Cargo.toml +++ b/cosmic-settings/Cargo.toml @@ -54,6 +54,10 @@ iced_winit.workspace = true locale1 = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true } sysinfo = { version = "=0.38.0", optional = true } mime-apps = { package = "cosmic-mime-apps", git = "https://github.com/pop-os/cosmic-mime-apps", features = ["tokio"], optional = true } +# Intégration OpenLogi (page Logitech des périphériques d'entrée). +openlogi-core = { path = "../../OpenLogi/crates/openlogi-core" } +openlogi-ipc = { path = "../../OpenLogi/crates/openlogi-ipc" } +tarpc = { version = "0.38", features = ["serde1", "tokio1"] } notify = "8.2.0" nmrs = { version = "3.5.2", optional = true } regex = "1.13.1" @@ -68,7 +72,7 @@ slotmap = "1.1.1" static_init = "1.0.4" sunrise = "3.0.0" timedate-zbus = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true } -tokio = { workspace = true, features = ["fs", "io-util", "process", "sync"] } +tokio = { workspace = true, features = ["fs", "io-util", "process", "sync", "time"] } tracing = "0.1.44" tracing-subscriber = { version = "0.3.23", features = ["env-filter"] } udev = { version = "0.9.3", optional = true } diff --git a/cosmic-settings/src/app.rs b/cosmic-settings/src/app.rs index 5268ecf..a1bb8b6 100644 --- a/cosmic-settings/src/app.rs +++ b/cosmic-settings/src/app.rs @@ -515,6 +515,13 @@ impl cosmic::Application for SettingsApp { } } + #[cfg(feature = "page-input")] + crate::pages::Message::Logitech(message) => { + if let Some(page) = self.pages.page_mut::() { + return page.update(message).map(Into::into); + } + } + #[cfg(feature = "page-input")] crate::pages::Message::KeyboardShortcuts(message) => { if let Some(page) = self.pages.page_mut::() { diff --git a/cosmic-settings/src/pages/input/logitech.rs b/cosmic-settings/src/pages/input/logitech.rs new file mode 100644 index 0000000..6db0250 --- /dev/null +++ b/cosmic-settings/src/pages/input/logitech.rs @@ -0,0 +1,551 @@ +// Copyright 2026 System76 +// SPDX-License-Identifier: GPL-3.0-only +// +// Page « Logitech » — intégration OpenLogi dans les périphériques d'entrée. +// Pilotée par l'agent OpenLogi (openlogi-agent) via son IPC tarpc local. +// Les réglages permanents (remappage complet, profils) restent dans la GUI +// OpenLogi ; cette page couvre le quotidien : pile, DPI, SmartShift. + +use cosmic::widget::{self, button, row, settings, text}; +use cosmic::{Apply, Element, Task}; +use cosmic_settings_page::{self as page, Section, section}; +use openlogi_core::device::{BatteryInfo, DeviceKind}; +use openlogi_core::hid::{ + DeviceRoute, Dpi, DpiInfo, SmartShiftAutoDisengage, SmartShiftMode, SmartShiftStatus, + SmartShiftThreshold, WriteError, +}; +use openlogi_ipc::{client, AgentClient, AgentSnapshot}; +use slotmap::SlotMap; +use tarpc::context; + +/// Clé d'identification stable d'un appareil dans les caches de la page. +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub struct DeviceKey { + pub receiver: String, + pub slot: u8, +} + +/// Ce que la page a réussi à lire pour un appareil donné. +/// `None` = non supporté (on n'affiche pas le réglage) ; `Some(Err)` = erreur. +#[derive(Clone, Debug, Default)] +pub struct DeviceReads { + pub dpi: Option>, + pub smartshift: Option>, +} + +/// Un appareil prêt à afficher : inventaire + lectures. +#[derive(Clone, Debug)] +pub struct DeviceEntry { + pub key: DeviceKey, + pub route: Option, + pub slot: u8, + pub name: String, + pub online: bool, + pub battery: Option, + pub pointer: bool, + pub reads: DeviceReads, +} + +#[derive(Clone, Debug)] +pub struct SnapshotData { + pub agent_version: String, + pub hook_installed: bool, + pub devices: Vec, + pub offline_count: usize, +} + +#[derive(Clone, Debug)] +pub enum Message { + /// Résultat d'un rafraîchissement complet (None = agent injoignable). + Snapshot(Option), + /// Écriture DPI envoyée à l'agent ; maj optimiste du cache. + SetDpi(DeviceKey, DeviceRoute, Dpi), + /// Écriture SmartShift ; maj optimiste du cache. + SetSmartShift(DeviceKey, DeviceRoute, bool), + /// Confirmation (ou échec) d'une écriture, par relecture. + WriteConfirmed(DeviceKey, DeviceReads), + /// Lancer la GUI OpenLogi complète. + OpenApp, + /// Démarrer l'agent s'il ne tourne pas. + StartAgent, + /// Rafraîchir. + Refresh, +} + +fn deliver(message: Message) -> crate::app::Message { + crate::app::Message::PageMessage(crate::pages::Message::Logitech(message)) +} + +#[derive(Default)] +pub struct Page { + entity: page::Entity, + on_enter_handle: Option, + /// `None` : pas encore de réponse de l'agent. `Some(None)` : agent absent. + pub snapshot: Option>, +} + +impl page::Page for Page { + fn set_id(&mut self, entity: page::Entity) { + self.entity = entity; + } + + fn on_enter(&mut self) -> Task { + self.start_refresh() + } + + fn on_leave(&mut self) -> Task { + if let Some(handle) = self.on_enter_handle.take() { + handle.abort(); + } + Task::none() + } + + fn content( + &self, + sections: &mut SlotMap>, + ) -> Option { + Some(vec![sections.insert(devices()), sections.insert(more())]) + } + + fn info(&self) -> page::Info { + page::Info::new("logitech", "input-mouse-symbolic") + .title(fl!("logitech")) + .description(fl!("logitech-comment")) + } +} + +impl page::AutoBind for Page {} + +impl Page { + pub fn update(&mut self, message: Message) -> Task { + match message { + Message::Snapshot(data) => { + self.snapshot = Some(data); + Task::none() + } + + Message::SetDpi(key, route, dpi) => { + let write_route = route.clone(); + Self::write(write_route, key, move |client| { + let dpi = dpi; + async move { + client + .set_dpi(context::current(), route.clone(), dpi) + .await + } + }) + } + + Message::SetSmartShift(key, route, enabled) => { + let previous = self + .snapshot + .as_ref() + .and_then(Option::as_ref) + .and_then(|s| s.devices.iter().find(|d| d.key == key)) + .and_then(|d| d.reads.smartshift.clone()) + .and_then(|r| r.ok()); + + // SmartShift activé : molette libre avec seuil de bascule. + // Désactivé : cliquet permanent (comme Options+). + let status = match (enabled, previous) { + (true, Some(previous)) => SmartShiftStatus { + mode: SmartShiftMode::Free, + ..previous + }, + (false, Some(previous)) => SmartShiftStatus { + mode: SmartShiftMode::Ratchet, + auto_disengage: SmartShiftAutoDisengage::Permanent, + ..previous + }, + (true, None) => SmartShiftStatus { + mode: SmartShiftMode::Free, + auto_disengage: SmartShiftAutoDisengage::Threshold( + SmartShiftThreshold::from_rounded(10.0), + ), + tunable_torque: None, + }, + (false, None) => SmartShiftStatus { + mode: SmartShiftMode::Ratchet, + auto_disengage: SmartShiftAutoDisengage::Permanent, + tunable_torque: None, + }, + }; + + let write_route = route.clone(); + Self::write(write_route, key, move |client| { + let status = status; + async move { + client + .set_smartshift(context::current(), route.clone(), status) + .await + } + }) + } + + Message::WriteConfirmed(..) => Task::none(), + + Message::OpenApp => { + if let Err(err) = std::process::Command::new("openlogi-desktop").spawn() { + tracing::warn!("failed to launch openlogi-desktop: {err}"); + } + Task::none() + } + + Message::StartAgent => Task::future(async move { + _ = tokio::process::Command::new("systemctl") + .args(["--user", "start", "openlogi-agent.service"]) + .status() + .await; + // Laisse le socket de l'agent s'ouvrir avant de réessayer. + tokio::time::sleep(std::time::Duration::from_millis(800)).await; + deliver(Message::Refresh) + }), + + Message::Refresh => { + self.start_refresh().map(crate::app::Message::PageMessage) + } + } + } + + /// Rafraîchit l'inventaire : connexion à l'agent, snapshot, puis lectures + /// DPI/SmartShift de chaque appareil pointeur en ligne. + fn start_refresh(&mut self) -> Task { + if let Some(handle) = self.on_enter_handle.take() { + handle.abort(); + } + + let (task, handle) = Task::future(async move { + crate::pages::Message::Logitech(Message::Snapshot(fetch_snapshot().await)) + }) + .abortable(); + + self.on_enter_handle = Some(handle); + task + } + + /// Envoie une écriture à l'agent, puis relit l'appareil pour confirmer. + fn write(route: DeviceRoute, key: DeviceKey, f: F) -> Task + where + F: FnOnce(AgentClient) -> Fut + Send + 'static, + Fut: std::future::Future< + Output = Result, tarpc::client::RpcError>, + > + Send, + { + let read_route = route.clone(); + + Task::future(async move { + if let Ok(connection) = client::connect().await { + let result = f(connection.client).await; + if !matches!(result, Ok(Ok(()))) { + tracing::warn!("openlogi write failed: {result:?}"); + } + } + + // Relit l'appareil : la valeur affichée redevient la vérité. + let mut reads = DeviceReads::default(); + if let Ok(connection) = client::connect().await { + reads.dpi = read_dpi(&connection.client, &read_route).await; + reads.smartshift = read_smartshift(&connection.client, &read_route).await; + } + deliver(Message::WriteConfirmed(key, reads)) + }) + } +} + +/// Une erreur qui signale « l'appareil n'a pas cette fonctionnalité » : +/// le réglage correspondant ne doit simplement pas s'afficher. +fn is_unsupported(err: &WriteError) -> bool { + matches!( + err, + WriteError::FeatureUnsupported { .. } | WriteError::EmptyDpiList + ) +} + +async fn read_dpi(client: &AgentClient, route: &DeviceRoute) -> Option> { + match client.read_dpi(context::current(), route.clone()).await { + Ok(Ok(info)) => Some(Ok(info)), + Ok(Err(err)) if is_unsupported(&err) => None, + Ok(Err(err)) => Some(Err(err.to_string())), + Err(err) => Some(Err(err.to_string())), + } +} + +async fn read_smartshift( + client: &AgentClient, + route: &DeviceRoute, +) -> Option> { + match client + .read_smartshift(context::current(), route.clone()) + .await + { + Ok(Ok(status)) => Some(Ok(status)), + Ok(Err(err)) if is_unsupported(&err) => None, + Ok(Err(err)) => Some(Err(err.to_string())), + Err(err) => Some(Err(err.to_string())), + } +} + +/// Connexion + snapshot + lectures par appareil, en une passe. +async fn fetch_snapshot() -> Option { + let connection = client::connect().await.ok()?; + let AgentSnapshot { + status, inventory, .. + } = connection.client.snapshot(context::current()).await.ok()?; + + let mut devices = Vec::new(); + let mut offline_count = 0usize; + + for inv in &inventory { + let receiver = inv + .receiver + .unique_id + .clone() + .unwrap_or_else(|| inv.receiver.name.clone()); + + for paired in &inv.paired { + if !paired.online { + offline_count += 1; + } + + let route = DeviceRoute::device_route_for(inv, paired.slot); + let pointer = paired.kind == DeviceKind::Mouse; + + // Ne parle qu'aux pointeurs en ligne, via leur route. + let reads = if paired.online && pointer { + match &route { + Some(route) => DeviceReads { + dpi: read_dpi(&connection.client, route).await, + smartshift: read_smartshift(&connection.client, route).await, + }, + None => DeviceReads::default(), + } + } else { + DeviceReads::default() + }; + + devices.push(DeviceEntry { + key: DeviceKey { + receiver: receiver.clone(), + slot: paired.slot, + }, + route, + slot: paired.slot, + name: paired.codename.clone().unwrap_or_else(|| { + fl!("logitech", "device-slot", slot = u32::from(paired.slot)) + }), + online: paired.online, + battery: paired.battery.clone(), + pointer, + reads, + }); + } + } + + Some(SnapshotData { + agent_version: status.agent_version, + hook_installed: status.hook_installed, + devices, + offline_count, + }) +} + +/// La page ne se lie que si du matériel Logitech est présent (comme le +/// touchpad) : périphérique HID dont le vendor ID est 046d. +pub fn system_has_logitech() -> bool { + let Ok(mut enumerator) = udev::Enumerator::new() else { + return false; + }; + + let _res = enumerator.match_subsystem("hid"); + + let Ok(mut devices) = enumerator.scan_devices() else { + return false; + }; + + devices.any(|device| { + device + .property_value("HID_ID") + .and_then(|value| value.to_str()) + .is_some_and(|id| { + // HID_ID = "bustype:vendor:product" en hexadécimal. + id.split(':') + .nth(1) + .is_some_and(|vendor| vendor.eq_ignore_ascii_case("0000046D")) + }) + }) +} + +fn devices() -> Section { + crate::slab!(descriptions { + dpi = fl!("logitech", "dpi"); + smartshift = fl!("logitech", "smartshift"); + smartshift_desc = fl!("logitech", "smartshift-desc"); + battery = fl!("logitech", "battery"); + offline = fl!("logitech", "offline"); + online = fl!("logitech", "online"); + no_agent = fl!("logitech", "no-agent"); + no_agent_desc = fl!("logitech", "no-agent-desc"); + start_agent = fl!("logitech", "start-agent"); + refresh = fl!("logitech", "refresh"); + no_devices = fl!("logitech", "no-devices"); + }); + + Section::default() + .title(fl!("logitech")) + .descriptions(descriptions) + .view::(move |binder, _page, section| { + let descriptions = §ion.descriptions; + let logitech = binder.page::().expect("logitech page not found"); + + let mut list = settings::section().title(§ion.title); + + match &logitech.snapshot { + // Pas encore de réponse : proposition de rafraîchissement. + None => { + list = list.add(settings::item( + "", + button::text(&descriptions[refresh]) + .on_press(crate::pages::Message::Logitech(Message::Refresh)), + )); + } + // Agent injoignable : explication + remise en route. + Some(None) => { + let actions = row(vec![ + button::text(&descriptions[start_agent]) + .on_press(crate::pages::Message::Logitech(Message::StartAgent)) + .into(), + button::text(fl!("logitech", "open-app")) + .on_press(crate::pages::Message::Logitech(Message::OpenApp)) + .into(), + ]) + .spacing(8); + + list = list + .add(settings::item( + &descriptions[no_agent], + text::body(&descriptions[no_agent_desc]), + )) + .add(settings::item("", actions)); + } + Some(Some(snapshot)) => { + if snapshot.devices.is_empty() { + list = list + .add(settings::item(&descriptions[no_devices], text::body(""))); + } + + for device in &snapshot.devices { + let mut details = Vec::new(); + if let Some(batt) = &device.battery { + details + .push(format!("{} · {} %", descriptions[battery], batt.percentage)); + } + details.push(if device.online { + descriptions[online].to_string() + } else { + descriptions[offline].to_string() + }); + let details = details.join(" · "); + + let Some(route) = device.route.clone() else { + list = list + .add(settings::item(&device.name, text::body(details))); + continue; + }; + + let item; + + if let Some(Ok(dpi_info)) = &device.reads.dpi { + let dpi_info = dpi_info.clone(); + let values: Vec = dpi_info + .capabilities + .values() + .iter() + .map(|dpi| dpi.to_string()) + .collect(); + let selected = dpi_info + .capabilities + .values() + .iter() + .position(|dpi| *dpi == dpi_info.current); + + let key = device.key.clone(); + let dpi_route = route.clone(); + let dpi_control = widget::dropdown(values, selected, move |index| { + let Some(dpi) = + dpi_info.capabilities.values().get(index).copied() + else { + return crate::pages::Message::Logitech(Message::Refresh); + }; + crate::pages::Message::Logitech(Message::SetDpi( + key.clone(), + dpi_route.clone(), + dpi, + )) + }); + + item = settings::item::builder(device.name.clone()) + .description(details) + .control(dpi_control); + } else { + item = settings::item::builder(device.name.clone()) + .description(details) + .control(text::body("")); + } + + list = list.add(item); + + if let Some(Ok(status)) = &device.reads.smartshift { + let status = *status; + let enabled = status.mode == SmartShiftMode::Free; + let key = device.key.clone(); + + list = list.add( + settings::item::builder(&descriptions[smartshift]) + .description(&descriptions[smartshift_desc]) + .toggler( + enabled, + move |value| { + crate::pages::Message::Logitech( + Message::SetSmartShift( + key.clone(), + route.clone(), + value, + ), + ) + }, + ), + ); + } + } + } + } + + list.apply(Element::from) + }) +} + +fn more() -> Section { + crate::slab!(descriptions { + open_app = fl!("logitech", "open-app"); + open_app_desc = fl!("logitech", "open-app-desc"); + }); + + Section::default() + .title(fl!("logitech", "more")) + .descriptions(descriptions) + .view::(move |_binder, _page, section| { + let descriptions = §ion.descriptions; + + settings::section() + .title(§ion.title) + .add( + settings::item::builder(&descriptions[open_app]) + .description(&descriptions[open_app_desc]) + .control(button::text(&descriptions[open_app]).on_press( + crate::pages::Message::Logitech(Message::OpenApp), + )), + ) + .apply(Element::from) + }) + .into() +} diff --git a/cosmic-settings/src/pages/input/mod.rs b/cosmic-settings/src/pages/input/mod.rs index 10613dc..b132918 100644 --- a/cosmic-settings/src/pages/input/mod.rs +++ b/cosmic-settings/src/pages/input/mod.rs @@ -12,6 +12,7 @@ use cosmic_settings_page as page; use tracing::error; pub mod keyboard; +pub mod logitech; pub mod mouse; pub mod touchpad; @@ -194,6 +195,12 @@ impl page::AutoBind for Page { fn sub_pages(page: page::Insert) -> page::Insert { let insert = page.sub_page::().sub_page::(); + let insert = if logitech::system_has_logitech() { + insert.sub_page::() + } else { + insert + }; + if system_has_touchpad() { insert.sub_page::() } else { diff --git a/cosmic-settings/src/pages/mod.rs b/cosmic-settings/src/pages/mod.rs index eaeeb82..2345b23 100644 --- a/cosmic-settings/src/pages/mod.rs +++ b/cosmic-settings/src/pages/mod.rs @@ -66,6 +66,8 @@ pub enum Message { #[cfg(feature = "page-input")] Keyboard(input::keyboard::Message), #[cfg(feature = "page-input")] + Logitech(input::logitech::Message), + #[cfg(feature = "page-input")] KeyboardShortcuts(input::keyboard::shortcuts::Message), #[cfg(feature = "page-legacy-applications")] LegacyApplications(applications::legacy_applications::Message), diff --git a/i18n/en/cosmic_settings.ftl b/i18n/en/cosmic_settings.ftl index ed24379..5fe67c4 100644 --- a/i18n/en/cosmic_settings.ftl +++ b/i18n/en/cosmic_settings.ftl @@ -994,6 +994,26 @@ mouse = Mouse .speed = Mouse speed .acceleration = Enable mouse acceleration +## Input: Logitech (OpenLogi) + +logitech = Logitech + .device-slot = Logitech device {$slot} + .battery = Battery + .online = Online + .offline = Offline + .dpi = Sensitivity (DPI) + .smartshift = SmartShift wheel + .smartshift-desc = Let the wheel switch freely to fast scrolling when spun + .no-agent = The OpenLogi agent is not running + .no-agent-desc = Start the agent to configure paired Logitech devices from here + .start-agent = Start the agent + .open-app = Open OpenLogi + .open-app-desc = Button remapping, profiles and advanced settings live in the OpenLogi app + .refresh = Refresh + .no-devices = No paired Logitech device was found + .more = More settings +logitech-comment = Logitech mice and keyboards, through OpenLogi + ## Input: Touchpad click-behavior = Click Behavior diff --git a/i18n/fr/cosmic_settings.ftl b/i18n/fr/cosmic_settings.ftl index eac17ae..91eda4b 100644 --- a/i18n/fr/cosmic_settings.ftl +++ b/i18n/fr/cosmic_settings.ftl @@ -766,6 +766,26 @@ mouse = Souris .speed = Vitesse de la souris .acceleration = Activer l'accélération de la souris +## Input: Logitech (OpenLogi) + +logitech = Logitech + .device-slot = Appareil Logitech {$slot} + .battery = Pile + .online = En ligne + .offline = Hors ligne + .dpi = Sensibilité (DPI) + .smartshift = Molette SmartShift + .smartshift-desc = La molette passe en défilement libre quand elle est lancée vite + .no-agent = L'agent OpenLogi n'est pas lancé + .no-agent-desc = Démarrez l'agent pour configurer les appareils Logitech appairés depuis ici + .start-agent = Démarrer l'agent + .open-app = Ouvrir OpenLogi + .open-app-desc = Remappage des boutons, profils et réglages avancés dans l'application OpenLogi + .refresh = Rafraîchir + .no-devices = Aucun appareil Logitech appairé trouvé + .more = Plus de réglages +logitech-comment = Souris et claviers Logitech, via OpenLogi + ## Input: Touchpad click-behavior = Comportement de clic