Inline some clones to make code more readable, add config, implement dimming -> locking -> sleeping into idle daemon, fix waiting on network thread, implement run menu with application launching and math evaluation,

This commit is contained in:
jmb05
2026-11-01 16:21:46 +01:00
parent 80b5cafb8c
commit a8fe99b345
14 changed files with 975 additions and 484 deletions

285
Cargo.lock generated
View File

@@ -377,9 +377,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]] [[package]]
name = "aws-lc-rs" name = "aws-lc-rs"
version = "1.18.2" version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faac5829c2b74c28f830747e7818ccfb684261b5f48a1118b1e2a13d36dfab13" checksum = "048006790404103c488e6c5616b3e7e702a71292aadda08e8f7698a9e0d2eb53"
dependencies = [ dependencies = [
"aws-lc-sys", "aws-lc-sys",
"untrusted 0.7.1", "untrusted 0.7.1",
@@ -388,9 +388,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-lc-sys" name = "aws-lc-sys"
version = "0.46.0" version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1622d8446a2d4b2ce0c7eefc73dd43a99779028d5ee5c2dd8073a658ba8a2bc" checksum = "8bfbe32ac36b3c428696ce315464181bc4029d689fd58e3f2a4e58c252242f52"
dependencies = [ dependencies = [
"cc", "cc",
"cmake", "cmake",
@@ -550,6 +550,12 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "block"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.12.1" version = "0.12.1"
@@ -607,6 +613,17 @@ dependencies = [
"zbus", "zbus",
] ]
[[package]]
name = "bstr"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f"
dependencies = [
"memchr",
"regex-automata",
"serde_core",
]
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.20.3" version = "3.20.3"
@@ -1029,9 +1046,9 @@ dependencies = [
[[package]] [[package]]
name = "crypto-bigint" name = "crypto-bigint"
version = "0.7.7" version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc907c45bb7e9ef8fc3778defd0f3cd8c81288f1b4e748eba064948f20c47122" checksum = "9c035cb64cba03dad2fb921f3e218785e8cf9a41790da5eb3a1400f62fa2be11"
dependencies = [ dependencies = [
"cpubits", "cpubits",
"ctutils", "ctutils",
@@ -1076,9 +1093,9 @@ dependencies = [
[[package]] [[package]]
name = "ctutils" name = "ctutils"
version = "0.4.5" version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d28fb6aa7dd680b3ffd1a3e64e115c177bdba63c0ae7f9cd48129ff05c4fa0" checksum = "a2376ffd6f96d8a9db59875b4b6b0237d6900b5baf64279714e05493ce64828a"
dependencies = [ dependencies = [
"cmov", "cmov",
"subtle", "subtle",
@@ -1242,9 +1259,9 @@ dependencies = [
[[package]] [[package]]
name = "der" name = "der"
version = "0.8.2" version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a878c850e9e421b20262e9b41f9c860e4785fa07541c266b62ff9d1ef998a80a" checksum = "6a996a108feb8b427a6e12fcf6af4aa83409b79d72c15337d7741368131ee004"
dependencies = [ dependencies = [
"const-oid", "const-oid",
"pem-rfc7468", "pem-rfc7468",
@@ -1417,7 +1434,7 @@ dependencies = [
[[package]] [[package]]
name = "edid" name = "edid"
version = "0.4.0" version = "0.4.0"
source = "git+https://github.com/jmb05/rust-edid#509d792617f521ea3797b70b547ac64a5fa35935" source = "git+https://github.com/jmb05/rust-edid#ab42e7fa8139b35ace725dad55ef8e698d4feb52"
dependencies = [ dependencies = [
"nom 8.0.0", "nom 8.0.0",
] ]
@@ -1430,9 +1447,9 @@ checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be"
[[package]] [[package]]
name = "elliptic-curve" name = "elliptic-curve"
version = "0.14.2" version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ad4496221fcfc21015fd8c0e26fcfab00504832dc0604f52acdd7bad2a8dc81" checksum = "900f37e7e18d74a2347389af6cd69e3a30d5cc9e697f40004865679aa75e9c7c"
dependencies = [ dependencies = [
"base16ct", "base16ct",
"crypto-bigint", "crypto-bigint",
@@ -1458,9 +1475,9 @@ checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.42" version = "0.8.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e985e0451871ad22fb8d2b6b076e2028a502a0d3950998c2c5c0a4f9b5d9679" checksum = "89253175e704ab869b04f9db224d38630e0211a53841a79181deae0889e169f5"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"core_detect", "core_detect",
@@ -1678,6 +1695,21 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "freedesktop-desktop-entry"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42a69ad6111a2b572cb41e0f3542d546386e841013cee7c45d705b61a15a87dd"
dependencies = [
"bstr",
"gettext-rs",
"log",
"memchr",
"thiserror 2.0.21",
"unicase",
"xdg",
]
[[package]] [[package]]
name = "fs_extra" name = "fs_extra"
version = "1.3.0" version = "1.3.0"
@@ -1897,6 +1929,26 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "gettext-rs"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9df5e3fa8c077b15fdfa8ce130e2bc73d663b8aaa6d51b71421b8659980b1d80"
dependencies = [
"gettext-sys",
"locale_config",
]
[[package]]
name = "gettext-sys"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4caa7e4460b196107cc1bb2629488e8b6dbb7f34f6683ea6753c4ac22f590785"
dependencies = [
"cc",
"temp-dir",
]
[[package]] [[package]]
name = "ghash" name = "ghash"
version = "0.6.0" version = "0.6.0"
@@ -2415,8 +2467,8 @@ dependencies = [
[[package]] [[package]]
name = "hyprland" name = "hyprland"
version = "0.4.0" version = "0.7.0"
source = "git+https://github.com/romanstingler/hyprland-rs-next?branch=master#80c1fc217c77873dc9f76c64419d1ed568410c06" source = "git+https://github.com/romanstingler/hyprland-rs-next?branch=master#7c2f69f8be78d89b58720bce7cad03b82e99baa3"
dependencies = [ dependencies = [
"async-stream", "async-stream",
"chardetng", "chardetng",
@@ -2434,8 +2486,8 @@ dependencies = [
[[package]] [[package]]
name = "hyprland-macros" name = "hyprland-macros"
version = "0.4.0" version = "0.7.0"
source = "git+https://github.com/romanstingler/hyprland-rs-next?branch=master#80c1fc217c77873dc9f76c64419d1ed568410c06" source = "git+https://github.com/romanstingler/hyprland-rs-next?branch=master#7c2f69f8be78d89b58720bce7cad03b82e99baa3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -2487,7 +2539,7 @@ dependencies = [
"js-sys", "js-sys",
"log", "log",
"wasm-bindgen", "wasm-bindgen",
"windows-core 0.61.2", "windows-core 0.62.2",
] ]
[[package]] [[package]]
@@ -2709,6 +2761,7 @@ dependencies = [
"crossbeam-channel", "crossbeam-channel",
"ddc-hi", "ddc-hi",
"eventfd", "eventfd",
"freedesktop-desktop-entry",
"futures-util", "futures-util",
"glib", "glib",
"glib-build-tools", "glib-build-tools",
@@ -2723,15 +2776,19 @@ dependencies = [
"jmb-logger-rs", "jmb-logger-rs",
"libc", "libc",
"libpulse-binding", "libpulse-binding",
"meval",
"mpris", "mpris",
"nmrs", "nmrs",
"notify", "notify",
"nucleo-matcher",
"rustsni", "rustsni",
"serde",
"serde_json", "serde_json",
"sysinfo 0.40.0", "sysinfo 0.40.1",
"thiserror 2.0.21", "thiserror 2.0.21",
"time-format", "time-format",
"tokio", "tokio",
"toml",
"wayland-client", "wayland-client",
"wayland-protocols", "wayland-protocols",
"zbus", "zbus",
@@ -2807,9 +2864,9 @@ dependencies = [
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.106" version = "0.3.107"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" checksum = "8dc09d2154098bbf4ef54eab6b117a3c24cbbffe94046b16ee6d6d4510584d3a"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"futures-util", "futures-util",
@@ -2982,6 +3039,19 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4744e383959f0db86ede514b809b1c53251889093803c05267acc7d4e7030d70" checksum = "4744e383959f0db86ede514b809b1c53251889093803c05267acc7d4e7030d70"
[[package]]
name = "locale_config"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934"
dependencies = [
"lazy_static",
"objc",
"objc-foundation",
"regex",
"winapi",
]
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.14" version = "0.4.14"
@@ -2993,9 +3063,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.34" version = "0.4.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" checksum = "5d15f86d12da44cd8d4ec3c73984537eb7f4cadcb9776a575a0a8a05ca11005c"
[[package]] [[package]]
name = "lru-slab" name = "lru-slab"
@@ -3079,6 +3149,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "matchers" name = "matchers"
version = "0.2.0" version = "0.2.0"
@@ -3179,6 +3258,16 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "meval"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f79496a5651c8d57cd033c5add8ca7ee4e3d5f7587a4777484640d9cb60392d9"
dependencies = [
"fnv",
"nom 1.2.4",
]
[[package]] [[package]]
name = "mime" name = "mime"
version = "0.3.17" version = "0.3.17"
@@ -3352,6 +3441,12 @@ dependencies = [
"zvariant", "zvariant",
] ]
[[package]]
name = "nom"
version = "1.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce"
[[package]] [[package]]
name = "nom" name = "nom"
version = "7.1.3" version = "7.1.3"
@@ -3417,6 +3512,16 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "nucleo-matcher"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf33f538733d1a5a3494b836ba913207f14d9d4a1d3cd67030c5061bdd2cac85"
dependencies = [
"memchr",
"unicode-segmentation",
]
[[package]] [[package]]
name = "num-bigint" name = "num-bigint"
version = "0.5.1" version = "0.5.1"
@@ -3525,6 +3630,26 @@ dependencies = [
"libloading 0.8.9", "libloading 0.8.9",
] ]
[[package]]
name = "objc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
dependencies = [
"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]] [[package]]
name = "objc2" name = "objc2"
version = "0.6.5" version = "0.6.5"
@@ -3601,6 +3726,15 @@ dependencies = [
"objc2-core-foundation", "objc2-core-foundation",
] ]
[[package]]
name = "objc_id"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
dependencies = [
"objc",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.21.4" version = "1.21.4"
@@ -3615,9 +3749,9 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]] [[package]]
name = "openssl-probe" name = "openssl-probe"
version = "0.2.1" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" checksum = "7e036b411754132fd6b74e94b74e9a13c5cf606fa93d1885c35860d3060820b1"
[[package]] [[package]]
name = "option-ext" name = "option-ext"
@@ -3678,9 +3812,9 @@ dependencies = [
[[package]] [[package]]
name = "pageant" name = "pageant"
version = "0.2.4" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0786409b5aaa884655c9b4065681db0835db0cc185a6fb19fb17a5d38656866" checksum = "e0bc922d9aa8cfcf598de27b3c6122cb2eeda9d6fe71527a22ebcb7f633540ff"
dependencies = [ dependencies = [
"base16ct", "base16ct",
"byteorder", "byteorder",
@@ -4242,9 +4376,9 @@ checksum = "263a3f48f01e7309e857138bd47f785585b4a005e8e56c6d2824ce91195999c3"
[[package]] [[package]]
name = "pulldown-cmark" name = "pulldown-cmark"
version = "0.13.5" version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "278e9268c93b9e498a2da46c34d90ba93a3255eb99009646398f34bc73ab36eb" checksum = "3db2be46835f013dd3f4fa7f5f7402c537787b54ebb6fe2b94bbdd7e2669bb7b"
dependencies = [ dependencies = [
"bitflags 2.13.2", "bitflags 2.13.2",
"memchr", "memchr",
@@ -4707,9 +4841,9 @@ dependencies = [
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "1.1.5" version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d" checksum = "e5e76742e5294153d4dde1207dbec5bdd81eef6fabdc0d2118babadabfd858aa"
dependencies = [ dependencies = [
"bitflags 2.13.2", "bitflags 2.13.2",
"errno", "errno",
@@ -5353,9 +5487,9 @@ dependencies = [
[[package]] [[package]]
name = "sysinfo" name = "sysinfo"
version = "0.40.0" version = "0.40.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "110e37d98880fd804cd47509b90a8444f7aaaab655ae625e83cd442a8bc2ebf2" checksum = "0e8a3edbe0394ce115f3917a00349dd242be09eb5a091ed509eaf67b55cfb8a8"
dependencies = [ dependencies = [
"libc", "libc",
"memchr", "memchr",
@@ -5430,6 +5564,12 @@ version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
[[package]]
name = "temp-dir"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83176759e9416cf81ee66cb6508dbfe9c96f20b8b56265a39917551c23c70964"
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.27.0" version = "3.27.0"
@@ -5660,9 +5800,9 @@ checksum = "06bdbd8cfc056b8d2e2e85f29b56a3bdbecb527cef81eb39e3e7b98af4652770"
[[package]] [[package]]
name = "tonic" name = "tonic"
version = "0.14.6" version = "0.14.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" checksum = "e2cdfc673aabd30fe8fee92e65837b644c37c5d47f0f97ae6d5e0f1237d15e1b"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"axum", "axum",
@@ -5681,6 +5821,7 @@ dependencies = [
"sync_wrapper", "sync_wrapper",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tokio-util",
"tower", "tower",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
@@ -5689,9 +5830,9 @@ dependencies = [
[[package]] [[package]]
name = "tonic-build" name = "tonic-build"
version = "0.14.6" version = "0.14.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c68f61875ac5293cf72e6c8cf0158086428c82c37229e98c840878f1706b0322" checksum = "ac6d916d206ed7bdda1778ddff9bda43303789c258877e7bc22fb44ee5c984b4"
dependencies = [ dependencies = [
"prettyplease", "prettyplease",
"proc-macro2", "proc-macro2",
@@ -5701,9 +5842,9 @@ dependencies = [
[[package]] [[package]]
name = "tonic-prost" name = "tonic-prost"
version = "0.14.6" version = "0.14.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" checksum = "eba464054e2226f182a34489b8b079bf4c6e3e7ab5fd7fc30e024f24aa92a484"
dependencies = [ dependencies = [
"bytes", "bytes",
"prost 0.14.4", "prost 0.14.4",
@@ -5712,9 +5853,9 @@ dependencies = [
[[package]] [[package]]
name = "tonic-prost-build" name = "tonic-prost-build"
version = "0.14.6" version = "0.14.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "654e5643eff75d7f8c99197ce1440ed19a3474eada74c12bbac488b2cafdae27" checksum = "2ea216ddc448e08e7839ab09e20e60b06b6a1c32745ccb212c0ebd161c199f1b"
dependencies = [ dependencies = [
"prettyplease", "prettyplease",
"proc-macro2", "proc-macro2",
@@ -5920,9 +6061,9 @@ checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.2.2" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" checksum = "ca718471876992059f251e33aafc334346ada08042b7661dd1fe313a9bced557"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
@@ -5988,9 +6129,12 @@ dependencies = [
[[package]] [[package]]
name = "utf8_iter" name = "utf8_iter"
version = "1.0.4" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" checksum = "af3bd9d3e571c26fcce9d033b87ea5ab061b8d2ffe9b7a5675d810d68f03edf0"
dependencies = [
"utf_types",
]
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
@@ -5998,6 +6142,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "utf_types"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a3b425286980a367278453f3b6d28cdcb134b574e5a35824a6ff5eebad50bb6"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.29.0" version = "1.29.0"
@@ -6089,12 +6239,11 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.129" version = "0.2.130"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" checksum = "a0181c14ddd6c7b8b0b0132a5f1ea2f32be0d893cd3ea97b6e3b28886b8a3d1c"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"once_cell",
"rustversion", "rustversion",
"wasm-bindgen-macro", "wasm-bindgen-macro",
"wasm-bindgen-shared", "wasm-bindgen-shared",
@@ -6102,9 +6251,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-futures" name = "wasm-bindgen-futures"
version = "0.4.79" version = "0.4.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cbab34de2d982e9b48e18d216d04c4a6f641066ff19ffb699980f591ee3610e" checksum = "5e14e0327cb5368ad60ccb22d04a9827a693c95a926d8d125e9fa39e0baec501"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"tokio", "tokio",
@@ -6113,9 +6262,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.129" version = "0.2.130"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" checksum = "6e931a02a39abdbf66966fc35902b5ed58344750a70885bed4ebb3d8eaf28ed9"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@@ -6123,9 +6272,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.129" version = "0.2.130"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" checksum = "2ca4b54280e455bd831e33a6b8e734baf432fe63fbc35c42ac14ce67a043a53a"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"proc-macro2", "proc-macro2",
@@ -6136,9 +6285,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.129" version = "0.2.130"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" checksum = "3b1f9c3798725016c6ee5f2f57869ec0831e8bbc0ab0310a86e787e97f41389e"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -6202,9 +6351,9 @@ dependencies = [
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.106" version = "0.3.107"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88261b9deccee56594c11a3460c462c41f58d148598fe70ad77070126a68aba4" checksum = "d7d179596ff5ec0f6de4db64d54011bde6289cc49603d3a6e4ce3f329207ab88"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
@@ -6684,7 +6833,7 @@ dependencies = [
"serde", "serde",
"thiserror 2.0.21", "thiserror 2.0.21",
"windows 0.62.2", "windows 0.62.2",
"windows-core 0.61.2", "windows-core 0.62.2",
] ]
[[package]] [[package]]
@@ -6719,14 +6868,20 @@ checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc"
[[package]] [[package]]
name = "xattr" name = "xattr"
version = "1.6.1" version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" checksum = "963d6fab92d5504b971dc8bd90ca743c2575e590bc8071ea5bc571598667ae08"
dependencies = [ dependencies = [
"libc", "libc",
"rustix", "rustix",
] ]
[[package]]
name = "xdg"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fb433233f2df9344722454bc7e96465c9d03bff9d77c248f9e7523fe79585b5"
[[package]] [[package]]
name = "xml-rs" name = "xml-rs"
version = "0.8.29" version = "0.8.29"
@@ -6819,9 +6974,9 @@ dependencies = [
[[package]] [[package]]
name = "zcheapstr" name = "zcheapstr"
version = "1.1.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473" checksum = "939f186d9ecebe044315a5cb6d6f78568bbc21152adc954d5a0c2f58bdbc9920"
dependencies = [ dependencies = [
"serde", "serde",
] ]

View File

@@ -43,6 +43,11 @@ ddc-hi = { git = "https://github.com/jmb05/ddc-hi-rs" }
wayland-protocols = { version = "0.32.13", features = ["staging", "client"] } wayland-protocols = { version = "0.32.13", features = ["staging", "client"] }
wayland-client = "0.31.15" wayland-client = "0.31.15"
zbus = "5.19.0" zbus = "5.19.0"
serde = { version = "1.0.229", features = ["derive"] }
freedesktop-desktop-entry = "0.8.4"
nucleo-matcher = "0.3.1"
meval = "0.2.0"
toml = "1.1.8"
#zbus = "6.1.0" #zbus = "6.1.0"
[build-dependencies] [build-dependencies]

4
jmb-bar.conf Normal file
View File

@@ -0,0 +1,4 @@
idle_timeout = 20
dim_timeout = 60
lock_timeout = 300
sleep_timeout = 320

View File

@@ -1,12 +1,13 @@
use crate::backend::{BackendThread, Registrator}; use crate::backend::{BackendThread, Registrator};
use crate::config::Config;
use crate::{APP_NAME, LOGGER}; use crate::{APP_NAME, LOGGER};
use crossbeam_channel::{Receiver, Sender}; use crossbeam_channel::{Receiver, Sender};
use jmb_logger_rs::*; use jmb_logger_rs::*;
use nmrs::raw::zbus::interface;
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error; use std::error::Error;
use std::process::Command;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant}; use std::time::Duration;
use tokio::sync::watch; use tokio::sync::watch;
use tokio::time::sleep; use tokio::time::sleep;
use wayland_client::globals::registry_queue_init; use wayland_client::globals::registry_queue_init;
@@ -17,12 +18,11 @@ use wayland_protocols::ext::idle_notify::v1::client::ext_idle_notification_v1::{
Event, ExtIdleNotificationV1, Event, ExtIdleNotificationV1,
}; };
use wayland_protocols::ext::idle_notify::v1::client::ext_idle_notifier_v1::ExtIdleNotifierV1; use wayland_protocols::ext::idle_notify::v1::client::ext_idle_notifier_v1::ExtIdleNotifierV1;
use zbus::conn::Builder; use zbus::interface;
use zbus::message::Header; use zbus::message::Header;
const SERVICE: &str = "org.freedesktop.ScreenSaver"; const SERVICE: &str = "org.freedesktop.ScreenSaver";
const OBJECT_PATH: &str = "/org/freedesktop/ScreenSaver"; const OBJECT_PATH: &str = "/org/freedesktop/ScreenSaver";
const LEGACY_OBJECT_PATH: &str = "/ScreenSaver";
struct WlState { struct WlState {
idle: Option<bool>, idle: Option<bool>,
@@ -53,14 +53,8 @@ impl Dispatch<ExtIdleNotificationV1, ()> for WlState {
_: &QueueHandle<Self>, _: &QueueHandle<Self>,
) { ) {
match event { match event {
Event::Idled => { Event::Idled => state.idle = Some(true),
println!("Idled: {:?}", Instant::now()); Event::Resumed => state.idle = Some(false),
state.idle = Some(true);
}
Event::Resumed => {
println!("Resumed: {:?}", Instant::now());
state.idle = Some(false);
}
_ => {} _ => {}
} }
if let Some(idle) = state.idle { if let Some(idle) = state.idle {
@@ -85,15 +79,15 @@ impl Dispatch<ExtIdleNotifierV1, (), WlState> for WlState {
struct Inhibitor { struct Inhibitor {
owner: String, owner: String,
application: String, application: String,
_reason: String, reason: String,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum IdleState { pub enum IdleState {
Init(Sender<IdleMessage>), Init(Sender<IdleMessage>),
Dimmed, Dimmed,
Inhibited, Inhibited(bool),
Caffeine, Caffeine(bool),
Active, Active,
} }
@@ -128,32 +122,40 @@ impl BackendThread<IdleState, Sender<IdleMessage>> for IdleDaemon {
} }
fn init(registration_rx: Receiver<Sender<IdleState>>) { fn init(registration_rx: Receiver<Sender<IdleState>>) {
let config = Config::load();
let (tx, rx) = crossbeam_channel::unbounded::<IdleMessage>(); let (tx, rx) = crossbeam_channel::unbounded::<IdleMessage>();
let tx_clone = tx.clone();
let instance = Arc::new(Mutex::new(Self { let instance = Arc::new(Mutex::new(Self {
subscribers: Vec::new(), subscribers: Vec::new(),
})); }));
let instance_clone = instance.clone(); std::thread::spawn({
std::thread::spawn(move || { let instance = instance.clone();
let tx = tx.clone();
move || {
while let Ok(sender) = registration_rx.recv() { while let Ok(sender) = registration_rx.recv() {
if sender.send(IdleState::Init(tx_clone.clone())).is_ok() { if sender.send(IdleState::Init(tx.clone())).is_ok() {
trace!(&LOGGER, "Added IdleDaemon Subscriber"); trace!(&LOGGER, "Added IdleDaemon Subscriber");
instance_clone instance
.lock() .lock()
.expect("Couldn't get lock") .expect("Couldn't get lock")
.subscribers .subscribers
.push(sender); .push(sender);
} }
} }
});
let (wl_tx, wl_rx) = watch::channel(false);
let (dbus_tx, dbus_rx) = watch::channel((false, false));
std::thread::spawn(move || {
if let Err(err) = Self::start_idle_notifier(wl_tx) {
error!(&LOGGER, "Idle Daemon unexpectedly quit, reason: {err}");
} }
}); });
std::thread::spawn(move || { let (wl_tx, wl_rx) = watch::channel(false);
let (dbus_tx, dbus_rx) = watch::channel(false);
std::thread::spawn({
let config = config.clone();
move || {
if let Err(err) = Self::start_idle_notifier(wl_tx, &config) {
error!(&LOGGER, "Idle Daemon unexpectedly quit, reason: {err}");
}
}
});
std::thread::spawn({
let instance = instance.clone();
move || {
let runtime = tokio::runtime::Builder::new_current_thread() let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all() .enable_all()
.build() .build()
@@ -166,35 +168,46 @@ impl BackendThread<IdleState, Sender<IdleMessage>> for IdleDaemon {
if idle_inhibitor.is_none() if idle_inhibitor.is_none()
&& let Ok(ii) = IdleInhibitor::inhibit_idle().await && let Ok(ii) = IdleInhibitor::inhibit_idle().await
{ {
instance.lock().unwrap().publish(IdleState::Caffeine(true));
idle_inhibitor = Some(ii); idle_inhibitor = Some(ii);
} }
} }
IdleMessage::Release => { IdleMessage::Release => {
if let Some(ii) = idle_inhibitor.take() if let Some(ii) = idle_inhibitor.take() {
&& let Err(e) = ii.uninhibit_idle().await match ii.uninhibit_idle().await {
{ Ok(_) => {
warn!(&LOGGER, "Error uninhibiting {e}"); instance
.lock()
.unwrap()
.publish(IdleState::Caffeine(false));
}
Err(e) => warn!(&LOGGER, "Error uninhibiting {e}"),
}
} }
} }
} }
} }
}); });
}
}); });
std::thread::spawn(move || { std::thread::spawn({
let instance_clone = instance.clone();
move || {
let runtime = tokio::runtime::Builder::new_current_thread() let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all() .enable_all()
.build() .build()
.expect("Failed to create Tokio runtime"); .expect("Failed to create Tokio runtime");
runtime.block_on(async move { runtime.block_on(async move {
let res = IdleDaemon::start_dbus(dbus_tx).await; let res = IdleDaemon::start_dbus(dbus_tx, instance_clone).await;
match res { match res {
Ok((_, _)) => { Ok((_, _)) => {
Self::run_idle_timer(instance, wl_rx, dbus_rx).await; Self::run_idle_timer(instance, wl_rx, dbus_rx, &config).await;
} }
Err(err) => error!(&LOGGER, "Error running D-Bus Screensaver: {err}"), Err(err) => error!(&LOGGER, "Error running D-Bus Screensaver: {err}"),
} }
}); });
}
}); });
} }
} }
@@ -215,20 +228,21 @@ impl IdleStage {
} }
} }
fn timeout(self) -> Duration { fn timeout(self, cfg: &Config) -> Duration {
match self { match self {
Self::Dim => Duration::from_secs(20), Self::Dim => Duration::from_secs(cfg.dim_timeout_delta()),
Self::TurnOff => Duration::from_secs(20), Self::TurnOff => Duration::from_secs(cfg.lock_timeout_delta()),
Self::Sleep => Duration::from_secs(20), Self::Sleep => Duration::from_secs(cfg.sleep_timeout_delta()),
} }
} }
} }
impl IdleDaemon { impl IdleDaemon {
async fn run_idle_timer( async fn run_idle_timer(
_instance: Arc<Mutex<Self>>, instance: Arc<Mutex<Self>>,
mut wl_rx: watch::Receiver<bool>, mut wl_rx: watch::Receiver<bool>,
mut dbus_rx: watch::Receiver<(bool, bool)>, mut dbus_rx: watch::Receiver<bool>,
config: &Config,
) { ) {
loop { loop {
if let Err(e) = wl_rx.changed().await { if let Err(e) = wl_rx.changed().await {
@@ -237,22 +251,26 @@ impl IdleDaemon {
} }
let wl_idle = *wl_rx.borrow_and_update(); let wl_idle = *wl_rx.borrow_and_update();
let dbus_state = *dbus_rx.borrow_and_update(); let dbus_state = *dbus_rx.borrow_and_update();
println!("Wayland idle state changed {wl_idle} (DBus state: {dbus_state:?})"); if wl_idle && !dbus_state {
if wl_idle && !dbus_state.0 {
println!("Going into idle cycle");
let mut stage = IdleStage::Dim; let mut stage = IdleStage::Dim;
loop { loop {
tokio::select! { tokio::select! {
_ = sleep(stage.timeout()) => { _ = sleep(stage.timeout(config)) => {
match stage {
IdleStage::Dim => instance.lock().unwrap().publish(IdleState::Dimmed),
IdleStage::TurnOff => _ = Command::new("hyprlock").spawn(),
IdleStage::Sleep => _ = Command::new("systemctl").arg("sleep").spawn(),
}
stage = stage.next(); stage = stage.next();
println!("Reached next stage {stage:?}");
} }
res = wl_rx.changed() => { res = wl_rx.changed() => {
if let Err(e) = res { if let Err(e) = res {
warn!(&LOGGER, "Error getting Wayland idle state {e}"); warn!(&LOGGER, "Error getting Wayland idle state {e}");
return; return;
} }
println!("WL state change"); if !*wl_rx.borrow_and_update() {
instance.lock().unwrap().publish(IdleState::Active);
}
break; break;
} }
res = dbus_rx.changed() => { res = dbus_rx.changed() => {
@@ -260,7 +278,6 @@ impl IdleDaemon {
warn!(&LOGGER, "Error getting D-Bus idle state {e}"); warn!(&LOGGER, "Error getting D-Bus idle state {e}");
return; return;
} }
println!("D-Bus state change");
break; break;
} }
} }
@@ -269,7 +286,7 @@ impl IdleDaemon {
} }
} }
fn start_idle_notifier(tx: watch::Sender<bool>) -> Result<(), Box<dyn Error>> { fn start_idle_notifier(tx: watch::Sender<bool>, config: &Config) -> Result<(), Box<dyn Error>> {
let conn = Connection::connect_to_env()?; let conn = Connection::connect_to_env()?;
let (globals, mut event_queue) = registry_queue_init::<WlState>(&conn)?; let (globals, mut event_queue) = registry_queue_init::<WlState>(&conn)?;
let queue_handle = event_queue.handle(); let queue_handle = event_queue.handle();
@@ -280,7 +297,12 @@ impl IdleDaemon {
let mut state = WlState { idle: None, tx }; let mut state = WlState { idle: None, tx };
let _notification = notifier.get_idle_notification(10_000, &seat, &queue_handle, ()); let _notification = notifier.get_idle_notification(
(config.idle_timeout() * 1000) as u32,
&seat,
&queue_handle,
(),
);
loop { loop {
event_queue.blocking_dispatch(&mut state)?; event_queue.blocking_dispatch(&mut state)?;
@@ -288,16 +310,18 @@ impl IdleDaemon {
} }
async fn start_dbus( async fn start_dbus(
tx: watch::Sender<(bool, bool)>, tx: watch::Sender<bool>,
daemon: Arc<Mutex<IdleDaemon>>,
) -> zbus::Result<(ScreenSaver, zbus::connection::Connection)> { ) -> zbus::Result<(ScreenSaver, zbus::connection::Connection)> {
let service = ScreenSaver::new(tx); let service = ScreenSaver::new(tx, daemon);
let connection = Builder::session()? let connection = zbus::Connection::session().await?;
.name(SERVICE)?
.serve_at(OBJECT_PATH, service.clone())? connection
.serve_at(LEGACY_OBJECT_PATH, service.clone())? .object_server()
.build() .at(OBJECT_PATH, service.clone())
.await?; .await?;
connection.request_name(SERVICE).await?;
info!(&LOGGER, "Providing {SERVICE}"); info!(&LOGGER, "Providing {SERVICE}");
@@ -347,22 +371,24 @@ impl IdleInhibitor {
#[derive(Clone)] #[derive(Clone)]
struct ScreenSaver { struct ScreenSaver {
inner: Arc<Mutex<Inner>>, inner: Arc<Mutex<Inner>>,
daemon: Arc<Mutex<IdleDaemon>>,
} }
struct Inner { struct Inner {
next_cookie: u32, next_cookie: u32,
inhibitors: HashMap<u32, Inhibitor>, inhibitors: HashMap<u32, Inhibitor>,
tx: watch::Sender<(bool, bool)>, tx: watch::Sender<bool>,
} }
impl ScreenSaver { impl ScreenSaver {
fn new(tx: watch::Sender<(bool, bool)>) -> Self { fn new(tx: watch::Sender<bool>, daemon: Arc<Mutex<IdleDaemon>>) -> Self {
Self { Self {
inner: Arc::new(Mutex::new(Inner { inner: Arc::new(Mutex::new(Inner {
next_cookie: 1, next_cookie: 1,
inhibitors: HashMap::new(), inhibitors: HashMap::new(),
tx, tx,
})), })),
daemon,
} }
} }
} }
@@ -399,21 +425,26 @@ impl ScreenSaver {
} }
}; };
let caffeine_flag = application_name.eq(APP_NAME); debug!(
&LOGGER,
debug!(&LOGGER, "Inhibitor added: {application_name} {cookie}"); "Inhibitor added - Name: '{application_name}' Reason: '{reason_for_inhibit}' Value: {cookie}"
);
inner.inhibitors.insert( inner.inhibitors.insert(
cookie, cookie,
Inhibitor { Inhibitor {
owner, owner,
application: application_name, application: application_name,
_reason: reason_for_inhibit, reason: reason_for_inhibit,
}, },
); );
self.daemon
.lock()
.unwrap()
.publish(IdleState::Inhibited(true));
inner.tx.send_modify(|state| { inner.tx.send_modify(|state| {
state.0 = !inner.inhibitors.is_empty(); *state = !inner.inhibitors.is_empty();
state.1 = caffeine_flag;
}); });
Ok(cookie) Ok(cookie)
} }
@@ -432,25 +463,25 @@ impl ScreenSaver {
if let Some(removed) = inner.inhibitors.remove(&cookie) { if let Some(removed) = inner.inhibitors.remove(&cookie) {
debug!( debug!(
&LOGGER, &LOGGER,
"Inhibitor removed: {} {cookie}", removed.application "Inhibitor removed - Name: '{}' Reason: '{}' Value: {cookie}",
removed.application,
removed.reason
); );
} }
let mut caffeine_flag = false;
for inhibitor in inner.inhibitors.values() { self.daemon
if inhibitor.application.eq(APP_NAME) { .lock()
caffeine_flag = true; .unwrap()
break; .publish(IdleState::Inhibited(!inner.inhibitors.is_empty()));
}
}
inner.tx.send_modify(|state| { inner.tx.send_modify(|state| {
state.0 = !inner.inhibitors.is_empty(); *state = !inner.inhibitors.is_empty();
state.1 = caffeine_flag;
}); });
Ok(()) Ok(())
} }
Some(_) => Err(zbus::fdo::Error::AccessDenied( Some(_) => Err(zbus::fdo::Error::AccessDenied(format!(
"Cookie belongs to another D-Bus client".into(), "Cookie {cookie} belongs to another D-Bus client"
)), ))),
None => Err(zbus::fdo::Error::Failed("Unknown inhibition cookie".into())), None => Err(zbus::fdo::Error::Failed("Unknown inhibition cookie".into())),
} }
} }

View File

@@ -161,6 +161,26 @@ impl BackendThread<NetworkState, UnboundedSender<NetworkCommand>> for NetworkBac
} }
}; };
std::thread::spawn({
let nm = nm.clone();
let mut instance = instance.clone();
move || {
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("Failed to create Tokio runtime");
runtime.block_on(async move {
while let Ok(tx) = registration_rx.recv() {
if tx.send(NetworkState::Init(instance.command_tx.clone())).is_ok() {
instance.subscribers.push(tx);
let _ = instance.send_all_networks(&nm).await;
let _ = instance.send_active_connection(&nm).await;
}
}
});
}});
let mut events = match nm.network_events().await { let mut events = match nm.network_events().await {
Ok(events) => events, Ok(events) => events,
Err(err) => { Err(err) => {
@@ -169,7 +189,6 @@ impl BackendThread<NetworkState, UnboundedSender<NetworkCommand>> for NetworkBac
} }
}; };
let mut debouncer = EventDebouncer { let mut debouncer = EventDebouncer {
duration: Duration::from_millis(200), duration: Duration::from_millis(200),
deadline: None, deadline: None,
@@ -177,13 +196,6 @@ impl BackendThread<NetworkState, UnboundedSender<NetworkCommand>> for NetworkBac
}; };
debouncer.sleep.as_mut().reset(Instant::now() + Duration::from_secs(86400)); debouncer.sleep.as_mut().reset(Instant::now() + Duration::from_secs(86400));
loop { loop {
while let Ok(tx) = registration_rx.try_recv() {
if tx.send(NetworkState::Init(instance.command_tx.clone())).is_ok() {
instance.subscribers.push(tx);
let _ = instance.send_all_networks(&nm).await;
let _ = instance.send_active_connection(&nm).await;
}
}
tokio::select! { tokio::select! {
command = command_rx.recv() => { command = command_rx.recv() => {
let Some(command) = command else { let Some(command) = command else {

View File

@@ -1,10 +1,10 @@
use crate::LOGGER; use crate::LOGGER;
use crate::backend::{BackendThread, SimpleBackendThread}; use crate::backend::{BackendThread, SimpleBackendThread};
use crossbeam_channel::{Receiver, Sender}; use crossbeam_channel::{Receiver, Sender};
use hyprland::data::{Client, Monitor, Workspaces}; use hyprland::data::{Client, CursorPosition, Monitor, Monitors, Workspaces};
use hyprland::event_listener::EventListener; use hyprland::event_listener::EventListener;
use hyprland::prelude::*; use hyprland::prelude::*;
use hyprland::shared::{MonitorId, WorkspaceId, WorkspaceType}; use hyprland::shared::{MonitorId, WorkspaceId};
use jmb_logger_rs::*; use jmb_logger_rs::*;
use std::io; use std::io;
use std::process::{Command, Output}; use std::process::{Command, Output};
@@ -24,7 +24,6 @@ pub struct HyprlandComms {
} }
impl SimpleBackendThread<WindowManagerEvent> for HyprlandComms { impl SimpleBackendThread<WindowManagerEvent> for HyprlandComms {
fn subscribers(&self) -> &Vec<Sender<WindowManagerEvent>> { fn subscribers(&self) -> &Vec<Sender<WindowManagerEvent>> {
&self.subscribers &self.subscribers
} }
@@ -38,9 +37,7 @@ impl SimpleBackendThread<WindowManagerEvent> for HyprlandComms {
let inst_clone = instance.clone(); let inst_clone = instance.clone();
listener.add_workspace_changed_handler(move |workspace| { listener.add_workspace_changed_handler(move |workspace| {
if let WorkspaceType::Regular(name) = workspace.name if let Ok(id) = workspace.name.parse::<i32>() {
&& let Ok(id) = name.parse::<i32>()
{
inst_clone inst_clone
.lock() .lock()
.expect("Couldn't get Subscribers") .expect("Couldn't get Subscribers")
@@ -50,17 +47,16 @@ impl SimpleBackendThread<WindowManagerEvent> for HyprlandComms {
let inst_clone = instance.clone(); let inst_clone = instance.clone();
listener.add_workspace_added_handler(move |workspace| { listener.add_workspace_added_handler(move |workspace| {
if let WorkspaceType::Regular(name) = workspace.name {
//unpacks the name of the workspace //unpacks the name of the workspace
if let Ok(wss) = Workspaces::get() { if let Ok(wss) = Workspaces::get() {
//gets all workspaces //gets all workspaces
if let Some(ws) = wss if let Some(ws) = wss
.to_vec() .to_vec()
.iter() .iter()
.find(|ws| ws.id().unwrap() == workspace.id) .find(|ws| ws.id().unwrap() == workspace.id().unwrap())
{ {
//find the current workspace Object from its id //find the current workspace Object from its id
if let Ok(id) = name.parse::<i32>() { if let Ok(id) = workspace.name.parse::<i32>() {
// parse the id as an int // parse the id as an int
inst_clone inst_clone
.lock() .lock()
@@ -72,14 +68,11 @@ impl SimpleBackendThread<WindowManagerEvent> for HyprlandComms {
} }
} }
} }
}
}); });
let inst_clone = instance.clone(); let inst_clone = instance.clone();
listener.add_workspace_deleted_handler(move |workspace| { listener.add_workspace_deleted_handler(move |workspace| {
if let WorkspaceType::Regular(name) = workspace.name if let Ok(id) = workspace.name.parse::<i32>() {
&& let Ok(id) = name.parse::<i32>()
{
inst_clone inst_clone
.lock() .lock()
.expect("Couldn't get Subscribers") .expect("Couldn't get Subscribers")
@@ -92,7 +85,9 @@ impl SimpleBackendThread<WindowManagerEvent> for HyprlandComms {
inst_clone inst_clone
.lock() .lock()
.expect("Couldn't get Subscribers") .expect("Couldn't get Subscribers")
.publish(WindowManagerEvent::WorkspaceChanged(evt.workspace_id)); .publish(WindowManagerEvent::WorkspaceChanged(
evt.workspace.id().unwrap(),
));
}); });
let inst_clone = instance.clone(); let inst_clone = instance.clone();
@@ -106,15 +101,22 @@ impl SimpleBackendThread<WindowManagerEvent> for HyprlandComms {
let inst_clone = instance.clone(); let inst_clone = instance.clone();
listener.add_workspace_moved_handler(move |event| { listener.add_workspace_moved_handler(move |event| {
if let Some(monitor_id) = Self::monitor_id_from_workspace_id(event.id) { if let Some(monitor_id) =
Self::monitor_id_from_workspace_id(event.workspace.id().unwrap())
{
inst_clone inst_clone
.lock() .lock()
.expect("Couldn't get Subscribers") .expect("Couldn't get Subscribers")
.publish(WindowManagerEvent::WorkspaceRemoved(event.id)); .publish(WindowManagerEvent::WorkspaceRemoved(
event.workspace.id().unwrap(),
));
inst_clone inst_clone
.lock() .lock()
.expect("Couldn't get Subscribers") .expect("Couldn't get Subscribers")
.publish(WindowManagerEvent::WorkspaceAdded(event.id, monitor_id)); .publish(WindowManagerEvent::WorkspaceAdded(
event.workspace.id().unwrap(),
monitor_id,
));
} }
}); });
@@ -160,7 +162,7 @@ impl HyprlandComms {
None None
} }
pub fn _current_monitor() -> MonitorId { pub fn current_monitor() -> MonitorId {
if let Ok(monitor) = Monitor::get_active() { if let Ok(monitor) = Monitor::get_active() {
return monitor.id; return monitor.id;
} }
@@ -197,4 +199,22 @@ impl HyprlandComms {
.arg(format!("hyprctl dispatch \"{dispatcher}\"")) .arg(format!("hyprctl dispatch \"{dispatcher}\""))
.output() .output()
} }
pub fn cursor_monitor() -> Option<MonitorId> {
if let Ok(pos) = CursorPosition::get()
&& let Ok(monitors) = Monitors::get()
{
for monitor in monitors {
if pos.x > monitor.x as i64
&& pos.x < (monitor.x as i64 + monitor.width as i64)
&& pos.y > monitor.y as i64
&& pos.y < (monitor.y as i64 + monitor.height as i64)
{
return Some(monitor.id);
}
}
}
None
}
} }

59
src/config.rs Normal file
View File

@@ -0,0 +1,59 @@
use serde::Deserialize;
use std::fs;
#[derive(Deserialize, Debug, Clone)]
pub(crate) struct Config {
idle_timeout: u64,
dim_timeout: u64,
lock_timeout: u64,
sleep_timeout: u64,
}
impl Default for Config {
fn default() -> Self {
Self {
idle_timeout: 20,
dim_timeout: 60,
lock_timeout: 300,
sleep_timeout: 320,
}
}
}
impl Config {
pub fn load() -> Config {
fs::read_to_string("jmb-bar.conf")
.ok()
.map(|s| toml::from_str::<Config>(&s).ok())
.flatten()
.unwrap_or_default()
}
pub fn idle_timeout(&self) -> u64 {
self.idle_timeout
}
pub fn dim_timeout(&self) -> u64 {
self.dim_timeout
}
pub fn lock_timeout(&self) -> u64 {
self.lock_timeout
}
pub fn sleep_timeout(&self) -> u64 {
self.sleep_timeout
}
pub fn dim_timeout_delta(&self) -> u64 {
self.dim_timeout - self.idle_timeout
}
pub fn lock_timeout_delta(&self) -> u64 {
self.lock_timeout - self.dim_timeout_delta()
}
pub fn sleep_timeout_delta(&self) -> u64 {
self.sleep_timeout - self.lock_timeout_delta()
}
}

View File

@@ -1,9 +1,11 @@
use crate::backend::Backend; use crate::backend::Backend;
mod backend; mod backend;
mod config;
mod css; mod css;
mod ctl; mod ctl;
mod ui; mod ui;
use crate::config::Config;
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Application, CssProvider, gdk, gio}; use gtk::{Application, CssProvider, gdk, gio};

View File

@@ -39,7 +39,7 @@ pub fn create_bars(app: &Application, backend: Backend) {
monitor, monitor,
create_and_show_bar( create_and_show_bar(
app_clone.clone(), app_clone.clone(),
&display, display.clone(),
monitor, monitor,
action_centre.clone(), action_centre.clone(),
channels, channels,
@@ -61,11 +61,11 @@ pub fn create_bars(app: &Application, backend: Backend) {
let action_centre = action_centre.clone(); let action_centre = action_centre.clone();
let channels = backend_clone.subscribe_all(); let channels = backend_clone.subscribe_all();
let bars = bars.clone(); let bars = bars.clone();
let display = display.clone();
glib::idle_add_local_once(move || { glib::idle_add_local_once(move || {
let display = Display::default().expect("Couldn't find GDK Display");
bars.lock().expect("Couldn't get active bars").insert( bars.lock().expect("Couldn't get active bars").insert(
pos, pos,
create_and_show_bar(app_clone, &display, pos, action_centre, channels), create_and_show_bar(app_clone, display, pos, action_centre, channels),
); );
}); });
} }
@@ -74,7 +74,7 @@ pub fn create_bars(app: &Application, backend: Backend) {
let mut ctl_channel = backend.subscribe(&[ChannelType::Control]); let mut ctl_channel = backend.subscribe(&[ChannelType::Control]);
let ctl_rx = ctl_channel.take_ctl_rx().unwrap(); let ctl_rx = ctl_channel.take_ctl_rx().unwrap();
let app_clone = app.clone(); let app_clone = app.clone();
let menu = RunMenu::create(app_clone); let mut menu = RunMenu::create(app_clone);
glib::timeout_add_local(Duration::from_millis(100), move || { glib::timeout_add_local(Duration::from_millis(100), move || {
while let Ok(cmd) = ctl_rx.try_recv() { while let Ok(cmd) = ctl_rx.try_recv() {
match cmd { match cmd {
@@ -89,7 +89,7 @@ pub fn create_bars(app: &Application, backend: Backend) {
fn create_and_show_bar( fn create_and_show_bar(
app: Application, app: Application,
display: &Display, display: Display,
monitor_idx: u32, monitor_idx: u32,
action_centre: ActionCentre, action_centre: ActionCentre,
mut channels: BackendChannels, mut channels: BackendChannels,
@@ -138,7 +138,7 @@ fn create_and_show_bar(
let title_button: Option<Button>; let title_button: Option<Button>;
let title = title_panel::build(); let title = title_panel::build();
let utilities = utilities_panel::build(&app, action_centre, channels, monitor_idx, display); let utilities = utilities_panel::build(&app, action_centre, channels, monitor_idx, &display);
let dock = gtk::Box::new(Orientation::Horizontal, 10); let dock = gtk::Box::new(Orientation::Horizontal, 10);
dock.add_css_class("dock"); dock.add_css_class("dock");

View File

@@ -111,33 +111,36 @@ impl ActionCentre {
window.set_child(Some(&scrolled)); window.set_child(Some(&scrolled));
let instance_clone = instance.clone();
let key_controller = gtk::EventControllerKey::new(); let key_controller = gtk::EventControllerKey::new();
key_controller.connect_key_pressed(move |_, key, _, _| { key_controller.connect_key_pressed({
let instance = instance.clone();
move |_, key, _, _| {
if key == gdk::Key::Escape { if key == gdk::Key::Escape {
instance_clone.hide_action_centre(); instance.hide_action_centre();
glib::Propagation::Stop glib::Propagation::Stop
} else { } else {
glib::Propagation::Proceed glib::Propagation::Proceed
} }
}
}); });
window.add_controller(key_controller); window.add_controller(key_controller);
let wifi_networks: Rc<RefCell<HashMap<String, WifiWidget>>> = let wifi_networks: Rc<RefCell<HashMap<String, WifiWidget>>> =
Rc::new(RefCell::new(HashMap::new())); Rc::new(RefCell::new(HashMap::new()));
let wifi_networks_clone = wifi_networks.clone(); window.connect_focus_widget_notify({
window.connect_focus_widget_notify(move |window| { let wifi_networks = wifi_networks.clone();
Self::focus_callback(&wifi_networks_clone, window) move |window| Self::focus_callback(&wifi_networks, window)
}); });
glib::timeout_add_local(Duration::from_millis(100), {
let window = window.clone();
let wifi_networks = wifi_networks.clone();
let net_rx = channels.take_net_rx().unwrap(); let net_rx = channels.take_net_rx().unwrap();
let window_clone = window.clone(); move || {
let wifi_networks_clone = wifi_networks.clone();
glib::timeout_add_local(Duration::from_millis(100), move || {
while let Ok(message) = net_rx.try_recv() { while let Ok(message) = net_rx.try_recv() {
match message { match message {
NetworkState::ChangeNetwork(Some(info)) => { NetworkState::ChangeNetwork(Some(info)) => {
for (name, widget) in wifi_networks_clone.borrow().iter() { for (name, widget) in wifi_networks.borrow().iter() {
if name.eq(&info.name) { if name.eq(&info.name) {
widget.add_css_class("connected"); widget.add_css_class("connected");
} else { } else {
@@ -147,24 +150,25 @@ impl ActionCentre {
} }
NetworkState::Networks(networks) => { NetworkState::Networks(networks) => {
for network in &networks { for network in &networks {
let borrowed_wifi_networks_clone = wifi_networks_clone.borrow_mut(); let borrowed_wifi_networks = wifi_networks.borrow_mut();
let widget = borrowed_wifi_networks_clone.get(&network.name); let widget = borrowed_wifi_networks.get(&network.name);
if let Some(widget) = widget { if let Some(widget) = widget {
let idx = ((network.signal as f32 / 100.0) let idx = ((network.signal as f32 / 100.0)
* (WIFI_ICONS.len() - 1) as f32 * (WIFI_ICONS.len() - 1) as f32
+ 0.5) as usize; + 0.5)
as usize;
widget.set_wifi_icon_from_name(WIFI_ICONS[idx]); widget.set_wifi_icon_from_name(WIFI_ICONS[idx]);
} else { } else {
let widget = Self::create_wifi_widget( let widget = Self::create_wifi_widget(
&channels, &channels,
network, network,
borrowed_wifi_networks_clone, borrowed_wifi_networks,
); );
content.append(&widget); content.append(&widget);
} }
} }
let mut removal: Vec<String> = Vec::new(); let mut removal: Vec<String> = Vec::new();
for (ssid, widget) in wifi_networks_clone.borrow().iter() { for (ssid, widget) in wifi_networks.borrow().iter() {
let network = &networks.iter().find(|n| n.name.eq(ssid)); let network = &networks.iter().find(|n| n.name.eq(ssid));
if network.is_none() { if network.is_none() {
content.remove(widget); content.remove(widget);
@@ -172,11 +176,11 @@ impl ActionCentre {
} }
} }
for ssid in removal { for ssid in removal {
wifi_networks_clone.borrow_mut().remove(&ssid); wifi_networks.borrow_mut().remove(&ssid);
} }
} }
NetworkState::Auth(ssid, auth) => { NetworkState::Auth(ssid, auth) => {
if let Some(widget) = Self::find_focused_widget(window_clone.as_ref()) { if let Some(widget) = Self::find_focused_widget(window.as_ref()) {
match auth { match auth {
AuthType::Password => { AuthType::Password => {
if widget.ssid().eq(&ssid) { if widget.ssid().eq(&ssid) {
@@ -189,7 +193,7 @@ impl ActionCentre {
} }
} }
NetworkState::ConnectFail(ssid, err) => { NetworkState::ConnectFail(ssid, err) => {
let mut networks = wifi_networks_clone.borrow_mut(); let mut networks = wifi_networks.borrow_mut();
if let Some(widget) = networks.get(&ssid) { if let Some(widget) = networks.get(&ssid) {
widget.stop_spinner(); widget.stop_spinner();
match err { match err {
@@ -203,7 +207,7 @@ impl ActionCentre {
} }
} }
NetworkState::ConnectSuccess(ssid) => { NetworkState::ConnectSuccess(ssid) => {
if let Some(widget) = wifi_networks_clone.borrow().get(&ssid) { if let Some(widget) = wifi_networks.borrow().get(&ssid) {
widget.stop_spinner(); widget.stop_spinner();
} }
} }
@@ -211,6 +215,7 @@ impl ActionCentre {
} }
} }
ControlFlow::Continue ControlFlow::Continue
}
}); });
instance instance
@@ -219,7 +224,7 @@ impl ActionCentre {
fn create_wifi_widget( fn create_wifi_widget(
channels: &BackendChannels, channels: &BackendChannels,
network: &NetworkInfo, network: &NetworkInfo,
mut borrowed_wifi_networks_clone: RefMut<HashMap<String, WifiWidget>>, mut borrowed_wifi_networks: RefMut<HashMap<String, WifiWidget>>,
) -> WifiWidget { ) -> WifiWidget {
let ssid = network.name.clone(); let ssid = network.name.clone();
let widget = WifiWidget::new(); let widget = WifiWidget::new();
@@ -229,7 +234,7 @@ impl ActionCentre {
widget.set_known(network.known); widget.set_known(network.known);
let idx = ((network.signal as f32 / 100.0) * (WIFI_ICONS.len() - 1) as f32 + 0.5) as usize; let idx = ((network.signal as f32 / 100.0) * (WIFI_ICONS.len() - 1) as f32 + 0.5) as usize;
widget.set_wifi_icon_from_name(WIFI_ICONS[idx]); widget.set_wifi_icon_from_name(WIFI_ICONS[idx]);
borrowed_wifi_networks_clone.insert(ssid.clone(), widget.clone()); borrowed_wifi_networks.insert(ssid.clone(), widget.clone());
let net_tx = channels.net_tx().unwrap(); let net_tx = channels.net_tx().unwrap();
widget.connect_clicked(clone!( widget.connect_clicked(clone!(
#[strong] #[strong]

View File

@@ -1,27 +1,44 @@
use gtk::prelude::{BoxExt, GtkWindowExt, WidgetExt}; use crate::LOGGER;
use crate::backend::window_manager_hypr::HyprlandComms;
use freedesktop_desktop_entry::{default_paths, get_languages_from_env};
use glib::clone;
use glib::prelude::Cast;
use gtk::gdk::{Display, Monitor};
use gtk::prelude::{
BoxExt, ButtonExt, DisplayExt, EditableExt, EntryExt, GtkWindowExt, ListModelExt, MonitorExt,
WidgetExt,
};
use gtk::{Align, Application, ApplicationWindow, Orientation, PolicyType, gdk}; use gtk::{Align, Application, ApplicationWindow, Orientation, PolicyType, gdk};
use gtk4_layer_shell::{KeyboardMode, Layer, LayerShell}; use gtk4_layer_shell::{Edge, KeyboardMode, Layer, LayerShell};
use jmb_logger_rs::*;
use nucleo_matcher::pattern::{CaseMatching, Normalization, Pattern};
use nucleo_matcher::{Config, Matcher};
use std::cell::RefCell;
use std::collections::HashMap;
use std::process::Command;
use std::rc::Rc;
#[derive(Clone)] #[derive(Clone)]
pub struct RunMenu { pub struct RunMenu {
window: ApplicationWindow, window: ApplicationWindow,
entry: gtk::Entry,
entries: Rc<RefCell<HashMap<String, Vec<String>>>>,
} }
impl RunMenu { impl RunMenu {
pub fn create(application: Application) -> Self { pub fn create(application: Application) -> Self {
let window = gtk::ApplicationWindow::builder() let window = ApplicationWindow::builder()
.application(&application) .application(&application)
.title("JmbRunMenu") .title("JmbRunMenu")
.default_width(500) .default_width(500)
.default_height(300)
.css_classes(["run-menu"]) .css_classes(["run-menu"])
.build(); .build();
window.init_layer_shell(); window.init_layer_shell();
window.set_layer(Layer::Overlay); window.set_layer(Layer::Overlay);
window.set_halign(Align::Center); window.set_halign(Align::Center);
window.set_valign(Align::Center); window.set_width_request(500);
window.set_size_request(500, 300); window.set_anchor(Edge::Top, true);
window.set_keyboard_mode(KeyboardMode::Exclusive); window.set_keyboard_mode(KeyboardMode::Exclusive);
let v_box = gtk::Box::new(Orientation::Vertical, 0); let v_box = gtk::Box::new(Orientation::Vertical, 0);
@@ -31,28 +48,109 @@ impl RunMenu {
let scrolled = gtk::ScrolledWindow::new(); let scrolled = gtk::ScrolledWindow::new();
scrolled.set_policy(PolicyType::Never, PolicyType::Automatic); scrolled.set_policy(PolicyType::Never, PolicyType::Automatic);
scrolled.set_vexpand(true); scrolled.set_vexpand(false);
scrolled.set_valign(Align::Fill); scrolled.set_propagate_natural_height(true);
scrolled.set_min_content_height(0);
scrolled.set_max_content_height(300);
let content_box = gtk::Box::new(Orientation::Vertical, 0); let content_box = gtk::Box::new(Orientation::Vertical, 0);
content_box.add_css_class("content-box"); content_box.add_css_class("content-box");
content_box.set_hexpand(true);
for _ in 0..10 {
let label = gtk::Label::new(Some("Test"));
content_box.append(&label);
}
scrolled.set_child(Some(&content_box)); scrolled.set_child(Some(&content_box));
v_box.set_vexpand(false);
v_box.set_valign(Align::Start);
v_box.append(&entry); v_box.append(&entry);
v_box.append(&scrolled); v_box.append(&scrolled);
window.set_child(Some(&v_box)); window.set_child(Some(&v_box));
let display = Display::default().expect("Couldn't get display");
let monitor = display
.monitors()
.item(HyprlandComms::current_monitor() as u32)
.expect("Couldn't get monitor")
.downcast::<Monitor>()
.ok();
window.set_monitor(monitor.as_ref());
if let Some(monitor) = monitor {
let geometry = monitor.geometry();
let (_, natural_height, _, _) =
window.child().unwrap().measure(Orientation::Vertical, -1);
println!("Monitor height: {}", geometry.height());
let top_margin = (geometry.height()) / 2;
window.set_margin(Edge::Top, top_margin - 100);
} else {
window.set_valign(Align::Center);
}
content_box
.observe_children()
.connect_items_changed(move |model, _, _, _| {
if model.n_items() == 0 {
println!("Hide");
} else {
println!("Reveal");
}
});
let window_clone = window.clone(); let window_clone = window.clone();
let instance = Self { let instance = Self {
window: window_clone, window: window_clone,
entry: entry.clone(),
entries: Rc::new(RefCell::new(HashMap::new())),
}; };
let instance_clone = instance.clone();
let matcher_rc = Rc::new(RefCell::new(Matcher::new(Config::DEFAULT)));
entry.connect_changed(clone!(
#[strong]
content_box,
move |e| {
while let Some(child) = content_box.first_child() {
content_box.remove(&child);
}
if e.text_length() > 0 {
let text = e.text();
if text.starts_with("=") {
if let Ok(eval) = meval::eval_str(&text[1..]) {
let label = gtk::Label::new(Some(&format!("{eval}")));
label.add_css_class("exec-button");
label.set_halign(Align::Start);
label.set_hexpand(true);
content_box.append(&label);
}
} else {
instance_clone.list_matching(e, &content_box, &mut matcher_rc.borrow_mut());
instance_clone.window.set_default_size(500, -1);
}
}
}
));
entry.connect_has_focus_notify(clone!(
#[strong]
content_box,
move |e| {
if let Some(child) = content_box.first_child() {
if e.has_focus() {
child.add_css_class("suggested");
} else {
child.remove_css_class("suggested");
}
}
}
));
entry.connect_activate(clone!(
#[strong]
content_box,
move |_| {
if let Some(child) = content_box.first_child()
&& let Ok(button) = child.downcast::<gtk::Button>()
{
button.activate();
}
}
));
let instance_clone = instance.clone(); let instance_clone = instance.clone();
let key_controller = gtk::EventControllerKey::new(); let key_controller = gtk::EventControllerKey::new();
key_controller.connect_key_pressed(move |_, key, _, _| { key_controller.connect_key_pressed(move |_, key, _, _| {
@@ -69,9 +167,82 @@ impl RunMenu {
pub fn hide(&self) { pub fn hide(&self) {
self.window.set_visible(false); self.window.set_visible(false);
self.entry.set_text("");
} }
pub fn show(&self) { pub fn show(&mut self) {
self.load_desktop_files();
self.window.present(); self.window.present();
} }
fn create_entry(&self, content_box: &gtk::Box, entry: &String) -> gtk::Button {
let entry_clone = entry.clone();
let button = gtk::Button::new();
button.set_halign(Align::Fill);
button.set_hexpand(true);
let label = gtk::Label::new(Some(entry));
label.set_halign(Align::Start);
button.set_child(Some(&label));
button.add_css_class("exec-button");
let entry_map = self.entries.clone();
let instance = self.clone();
button.connect_clicked(move |_| {
let map = entry_map.borrow();
let exec = map.get(&entry_clone);
if let Some(exec) = exec {
let executable = &exec[0];
if let Err(err) = Command::new(executable).args(&exec[1..]).spawn() {
warn!(&LOGGER, "Couldn't execute app: {err}");
}
instance.hide();
}
});
content_box.append(&button);
button
}
fn list_matching(
&self,
entry: &gtk::Entry,
content_box: &gtk::Box,
matcher: &mut Matcher,
) -> bool {
let keys: Vec<String> = self.entries.borrow().keys().map(|i| i.clone()).collect();
let mut first = true;
let mut added = false;
for (entry, _) in Pattern::parse(&entry.text(), CaseMatching::Ignore, Normalization::Smart)
.match_list(keys, matcher)
.iter()
{
let button = self.create_entry(content_box, &entry);
if first {
button.add_css_class("suggested");
first = false;
}
added = true;
}
added
}
fn load_desktop_files(&mut self) {
let locales = get_languages_from_env();
let entries = freedesktop_desktop_entry::Iter::new(default_paths())
.entries(Some(&locales))
.collect::<Vec<_>>();
let mut entry_map = self.entries.borrow_mut();
entry_map.clear();
for entry in entries {
if let Ok(exec) = entry.parse_exec() {
let name = entry.full_name(&locales);
if let Some(name) = name {
entry_map.insert(name.to_string(), exec);
}
}
}
}
} }

View File

@@ -78,15 +78,17 @@ pub fn build(
let media_tx = channels.media_tx().unwrap(); let media_tx = channels.media_tx().unwrap();
let changed_progr = Rc::new(Cell::new(false)); let changed_progr = Rc::new(Cell::new(false));
let changed_progr_clone = changed_progr.clone(); volume.connect_value_changed({
volume.connect_value_changed(move |slider, button, _| { let changed_progr = changed_progr.clone();
if !changed_progr_clone.get() { move |slider, button, _| {
if !changed_progr.get() {
let _ = media_tx.send(MediaCommand::Cmd( let _ = media_tx.send(MediaCommand::Cmd(
slider.value() as u8, slider.value() as u8,
button.is_active(), button.is_active(),
false, false,
)); ));
} }
}
}); });
let media_tx = channels.media_tx().unwrap(); let media_tx = channels.media_tx().unwrap();
volume.connect_clicked(move |slider, button, _| { volume.connect_clicked(move |slider, button, _| {
@@ -189,7 +191,7 @@ pub fn build(
let network_button = Button::new(); let network_button = Button::new();
network_button.add_css_class("network"); network_button.add_css_class("network");
let network_button_content = gtk::Box::new(Orientation::Horizontal, 0); let network_button_content = gtk::Box::new(Orientation::Horizontal, 0);
let network_icon = Image::from_file("../../icons/disconnected.svg"); let network_icon = Image::from_file("icons/disconnected.svg");
network_icon.add_css_class("network-icon"); network_icon.add_css_class("network-icon");
network_button_content.append(&network_icon); network_button_content.append(&network_icon);
let network_label = Label::new(None); let network_label = Label::new(None);
@@ -312,15 +314,16 @@ pub fn build(
ControlFlow::Continue ControlFlow::Continue
}); });
glib::timeout_add_local(Duration::from_millis(100), {
let media_rx = channels.take_media_rx().unwrap(); let media_rx = channels.take_media_rx().unwrap();
let br_rx = channels.take_br_rx().unwrap(); let br_rx = channels.take_br_rx().unwrap();
let net_rx = channels.take_net_rx().unwrap(); let net_rx = channels.take_net_rx().unwrap();
let stats_rx = channels.take_stats_rx().unwrap(); let stats_rx = channels.take_stats_rx().unwrap();
let netbird_rx = channels.take_netbird_rx().unwrap(); let netbird_rx = channels.take_netbird_rx().unwrap();
let idle_rx = channels.take_idle_rx().unwrap(); let idle_rx = channels.take_idle_rx().unwrap();
let monitor_clone = monitor.clone(); let monitor = monitor.clone();
let br_tx = channels.br_tx().unwrap(); let br_tx = channels.br_tx().unwrap();
glib::timeout_add_local(Duration::from_millis(100), move || { move || {
while let Ok(command) = media_rx.try_recv() { while let Ok(command) = media_rx.try_recv() {
if let MediaCommand::Cmd(vol, mute, bt) = command { if let MediaCommand::Cmd(vol, mute, bt) = command {
changed_progr.set(true); changed_progr.set(true);
@@ -350,8 +353,8 @@ pub fn build(
&& monitor.eq(&mon) && monitor.eq(&mon)
{ {
brightness.set_value(value as f64); brightness.set_value(value as f64);
let idx = let idx = ((value as f32 / 100.0) * (BRIGHTNESS_ICONS.len() - 1) as f32 + 0.5)
((value as f32 / 100.0) * (BRIGHTNESS_ICONS.len() - 1) as f32 + 0.5) as usize; as usize;
brightness brightness
.imp() .imp()
.main_icon .main_icon
@@ -362,8 +365,7 @@ pub fn build(
match state { match state {
IdleState::Dimmed => { IdleState::Dimmed => {
if !caffeine_button.has_css_class("idle") && monitor_valid { if !caffeine_button.has_css_class("idle") && monitor_valid {
let _ = let _ = br_tx.send(BrightnessMessage::Value(monitor.clone(), 20, true));
br_tx.send(BrightnessMessage::Value(monitor_clone.clone(), 20, true));
brightness.set_value(20.0); brightness.set_value(20.0);
} }
caffeine_button.add_css_class("idle"); caffeine_button.add_css_class("idle");
@@ -372,14 +374,16 @@ pub fn build(
} }
other => { other => {
caffeine_button.remove_css_class("idle"); caffeine_button.remove_css_class("idle");
caffeine_button.remove_css_class("inhibit");
caffeine_button.remove_css_class("on");
if monitor_valid { if monitor_valid {
let _ = br_tx.send(BrightnessMessage::Revert(monitor_clone.clone())); let _ = br_tx.send(BrightnessMessage::Revert(monitor.clone()));
} }
match other { match other {
IdleState::Caffeine => caffeine_button.add_css_class("on"), IdleState::Caffeine(true) => caffeine_button.add_css_class("on"),
IdleState::Inhibited => caffeine_button.add_css_class("inhibit"), IdleState::Inhibited(true) => caffeine_button.add_css_class("inhibit"),
IdleState::Caffeine(false) => caffeine_button.remove_css_class("on"),
IdleState::Inhibited(false) => {
caffeine_button.remove_css_class("inhibit")
}
IdleState::Active => {} IdleState::Active => {}
_ => {} _ => {}
} }
@@ -485,11 +489,13 @@ pub fn build(
let idx = ((stats.battery_charge as f32 / 100.0) * (BATTERY_ICONS.len() - 1) as f32 let idx = ((stats.battery_charge as f32 / 100.0) * (BATTERY_ICONS.len() - 1) as f32
+ 0.5) as usize; + 0.5) as usize;
let idx = idx.min(BATTERY_ICONS.len() - 1); let idx = idx.min(BATTERY_ICONS.len() - 1);
battery_widget.set_title(&format!("{} {}%", BATTERY_ICONS[idx], stats.battery_charge)); battery_widget
.set_title(&format!("{} {}%", BATTERY_ICONS[idx], stats.battery_charge));
wattage.set_label(&format!("{:.1}W", stats.battery_draw)); wattage.set_label(&format!("{:.1}W", stats.battery_draw));
remaining_time.set_label(&format!("{}h", format_remaining(stats.battery_time))); remaining_time.set_label(&format!("{}h", format_remaining(stats.battery_time)));
} }
ControlFlow::Continue ControlFlow::Continue
}
}); });
utilities utilities
} }

View File

@@ -125,10 +125,6 @@ button {
background: $background1; background: $background1;
} }
.caffeine.on {
background: $red;
}
.caffeine.idle { .caffeine.idle {
background: $yellow; background: $yellow;
} }
@@ -137,6 +133,10 @@ button {
background: $green; background: $green;
} }
.caffeine.on {
background: $red;
}
.stats { .stats {
padding: 0 6px; padding: 0 6px;
} }

View File

@@ -2,4 +2,25 @@
.run-menu { .run-menu {
background: $background; background: $background;
padding: 5px;
border-radius: 10px;
border: 1px solid $orange;
}
.exec-button {
padding: 3px;
border-radius: 5px;
border: 1px solid $background1;
}
.exec-button:focus {
border-color: $orange;
}
.suggested {
border-color: $orange;
}
.exec-button:active {
border-color: $orange;
} }