feat: add media controls and fixed icon resolution
- Implement Wayland pointer handling for interactive media buttons. - Add pure-Rust SVG loading via resvg. - Refactor status indicators for consistent icon and layout handling.
This commit is contained in:
Generated
+198
-3
@@ -72,6 +72,18 @@ version = "1.0.102"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayref"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-broadcast"
|
name = "async-broadcast"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
@@ -259,6 +271,12 @@ version = "0.21.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.22.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bindgen"
|
name = "bindgen"
|
||||||
version = "0.69.5"
|
version = "0.69.5"
|
||||||
@@ -606,6 +624,12 @@ dependencies = [
|
|||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "data-url"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dbus"
|
name = "dbus"
|
||||||
version = "0.9.10"
|
version = "0.9.10"
|
||||||
@@ -762,6 +786,15 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "euclid"
|
||||||
|
version = "0.22.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "event-listener"
|
name = "event-listener"
|
||||||
version = "2.5.3"
|
version = "2.5.3"
|
||||||
@@ -846,6 +879,12 @@ dependencies = [
|
|||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "float-cmp"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
@@ -1440,11 +1479,17 @@ dependencies = [
|
|||||||
"byteorder-lite",
|
"byteorder-lite",
|
||||||
"moxcms",
|
"moxcms",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"png",
|
"png 0.18.1",
|
||||||
"zune-core",
|
"zune-core",
|
||||||
"zune-jpeg",
|
"zune-jpeg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "imagesize"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.13.0"
|
version = "2.13.0"
|
||||||
@@ -1538,6 +1583,17 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kurbo"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
"euclid",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -1850,6 +1906,12 @@ version = "2.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pico-args"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
@@ -1879,6 +1941,19 @@ 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 = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "png"
|
||||||
|
version = "0.17.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"crc32fast",
|
||||||
|
"fdeflate",
|
||||||
|
"flate2",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "png"
|
name = "png"
|
||||||
version = "0.18.1"
|
version = "0.18.1"
|
||||||
@@ -2103,7 +2178,7 @@ version = "0.11.27"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
|
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64 0.21.7",
|
||||||
"bytes",
|
"bytes",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@@ -2138,6 +2213,29 @@ dependencies = [
|
|||||||
"winreg",
|
"winreg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "resvg"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "944d052815156ac8fa77eaac055220e95ba0b01fa8887108ca710c03805d9051"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"pico-args",
|
||||||
|
"rgb",
|
||||||
|
"svgtypes",
|
||||||
|
"tiny-skia",
|
||||||
|
"usvg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rgb"
|
||||||
|
version = "0.8.53"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.14"
|
version = "0.17.14"
|
||||||
@@ -2152,6 +2250,12 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roxmltree"
|
||||||
|
version = "0.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rpassword"
|
name = "rpassword"
|
||||||
version = "6.0.1"
|
version = "6.0.1"
|
||||||
@@ -2234,12 +2338,15 @@ dependencies = [
|
|||||||
"pangocairo",
|
"pangocairo",
|
||||||
"rand",
|
"rand",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"resvg",
|
||||||
"serde",
|
"serde",
|
||||||
"smithay-client-toolkit",
|
"smithay-client-toolkit",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tiny-skia",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml 1.0.6+spec-1.1.0",
|
"toml 1.0.6+spec-1.1.0",
|
||||||
"upower_dbus",
|
"upower_dbus",
|
||||||
|
"usvg",
|
||||||
"wayland-client",
|
"wayland-client",
|
||||||
"wayland-protocols",
|
"wayland-protocols",
|
||||||
"wayland-protocols-wlr",
|
"wayland-protocols-wlr",
|
||||||
@@ -2267,7 +2374,7 @@ version = "1.0.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
|
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64 0.21.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2416,6 +2523,21 @@ version = "0.3.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
|
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simplecss"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "siphasher"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.12"
|
version = "0.4.12"
|
||||||
@@ -2498,12 +2620,31 @@ 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 = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strict-num"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
||||||
|
dependencies = [
|
||||||
|
"float-cmp",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "svgtypes"
|
||||||
|
version = "0.15.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc"
|
||||||
|
dependencies = [
|
||||||
|
"kurbo",
|
||||||
|
"siphasher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "0.11.11"
|
version = "0.11.11"
|
||||||
@@ -2636,6 +2777,32 @@ dependencies = [
|
|||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiny-skia"
|
||||||
|
version = "0.11.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab"
|
||||||
|
dependencies = [
|
||||||
|
"arrayref",
|
||||||
|
"arrayvec",
|
||||||
|
"bytemuck",
|
||||||
|
"cfg-if",
|
||||||
|
"log",
|
||||||
|
"png 0.17.16",
|
||||||
|
"tiny-skia-path",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiny-skia-path"
|
||||||
|
version = "0.11.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"
|
||||||
|
dependencies = [
|
||||||
|
"arrayref",
|
||||||
|
"bytemuck",
|
||||||
|
"strict-num",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinystr"
|
name = "tinystr"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
@@ -2901,6 +3068,28 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "usvg"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"data-url",
|
||||||
|
"flate2",
|
||||||
|
"imagesize",
|
||||||
|
"kurbo",
|
||||||
|
"log",
|
||||||
|
"pico-args",
|
||||||
|
"roxmltree",
|
||||||
|
"simplecss",
|
||||||
|
"siphasher",
|
||||||
|
"strict-num",
|
||||||
|
"svgtypes",
|
||||||
|
"tiny-skia-path",
|
||||||
|
"xmlwriter",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8_iter"
|
name = "utf8_iter"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@@ -3578,6 +3767,12 @@ dependencies = [
|
|||||||
"bytemuck",
|
"bytemuck",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xmlwriter"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ bytemuck = "1.14"
|
|||||||
calloop = "0.13"
|
calloop = "0.13"
|
||||||
xkbcommon = "0.7"
|
xkbcommon = "0.7"
|
||||||
calloop-wayland-source = "0.3"
|
calloop-wayland-source = "0.3"
|
||||||
|
resvg = { version = "0.42", default-features = false }
|
||||||
|
usvg = { version = "0.42", default-features = false }
|
||||||
|
tiny-skia = "0.11"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["networking"]
|
default = ["networking"]
|
||||||
|
|||||||
+44
-1
@@ -35,6 +35,7 @@ use smithay_client_toolkit::{
|
|||||||
registry_handlers,
|
registry_handlers,
|
||||||
seat::{
|
seat::{
|
||||||
keyboard::{KeyEvent, KeyboardHandler, Modifiers},
|
keyboard::{KeyEvent, KeyboardHandler, Modifiers},
|
||||||
|
pointer::{PointerEvent, PointerEventKind, PointerHandler},
|
||||||
SeatHandler, SeatState,
|
SeatHandler, SeatState,
|
||||||
},
|
},
|
||||||
session_lock::{
|
session_lock::{
|
||||||
@@ -248,6 +249,8 @@ impl ProvidesRegistryState for WaylandLock {
|
|||||||
registry_handlers![OutputState, SeatState];
|
registry_handlers![OutputState, SeatState];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WaylandLock {}
|
||||||
|
|
||||||
impl CompositorHandler for WaylandLock {
|
impl CompositorHandler for WaylandLock {
|
||||||
fn scale_factor_changed(
|
fn scale_factor_changed(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -432,7 +435,7 @@ impl SeatHandler for WaylandLock {
|
|||||||
) {
|
) {
|
||||||
if capability == smithay_client_toolkit::seat::Capability::Keyboard {
|
if capability == smithay_client_toolkit::seat::Capability::Keyboard {
|
||||||
let _ = self.seat_state.get_keyboard_with_repeat(
|
let _ = self.seat_state.get_keyboard_with_repeat(
|
||||||
qh,
|
&qh,
|
||||||
&seat,
|
&seat,
|
||||||
None,
|
None,
|
||||||
self.loop_handle.clone(),
|
self.loop_handle.clone(),
|
||||||
@@ -441,6 +444,9 @@ impl SeatHandler for WaylandLock {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if capability == smithay_client_toolkit::seat::Capability::Pointer {
|
||||||
|
let _ = self.seat_state.get_pointer(&qh, &seat);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn remove_capability(
|
fn remove_capability(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -571,6 +577,43 @@ impl wayland_client::Dispatch<wayland_client::protocol::wl_registry::WlRegistry,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PointerHandler for WaylandLock {
|
||||||
|
fn pointer_frame(
|
||||||
|
&mut self,
|
||||||
|
_conn: &Connection,
|
||||||
|
_qh: &QueueHandle<Self>,
|
||||||
|
_pointer: &wayland_client::protocol::wl_pointer::WlPointer,
|
||||||
|
events: &[PointerEvent],
|
||||||
|
) {
|
||||||
|
for event in events {
|
||||||
|
if let PointerEventKind::Press { button, .. } = event.kind {
|
||||||
|
if button == 0x110 {
|
||||||
|
let (x, y) = event.position;
|
||||||
|
if let Ok(lm) = self.lock_manager.lock() {
|
||||||
|
for surface in &lm.surfaces {
|
||||||
|
if surface.matches_surface(&event.surface) {
|
||||||
|
for (action, rx, ry, rw, rh) in &surface.renderer.media_rects {
|
||||||
|
if x >= *rx && x <= rx + rw && y >= *ry && y <= ry + rh {
|
||||||
|
match action.as_str() {
|
||||||
|
"play" => self.system_manager.media_play_pause(),
|
||||||
|
"stop" => self.system_manager.media_stop(),
|
||||||
|
"next" => self.system_manager.media_next(),
|
||||||
|
"prev" => self.system_manager.media_prev(),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
smithay_client_toolkit::delegate_pointer!(WaylandLock);
|
||||||
smithay_client_toolkit::delegate_compositor!(WaylandLock);
|
smithay_client_toolkit::delegate_compositor!(WaylandLock);
|
||||||
smithay_client_toolkit::delegate_output!(WaylandLock);
|
smithay_client_toolkit::delegate_output!(WaylandLock);
|
||||||
smithay_client_toolkit::delegate_shm!(WaylandLock);
|
smithay_client_toolkit::delegate_shm!(WaylandLock);
|
||||||
|
|||||||
+393
-52
@@ -34,6 +34,7 @@ pub struct Renderer {
|
|||||||
media_stop_icon_surface: Option<ImageSurface>,
|
media_stop_icon_surface: Option<ImageSurface>,
|
||||||
media_play_icon_surface: Option<ImageSurface>,
|
media_play_icon_surface: Option<ImageSurface>,
|
||||||
media_next_icon_surface: Option<ImageSurface>,
|
media_next_icon_surface: Option<ImageSurface>,
|
||||||
|
pub media_rects: Vec<(String, f64, f64, f64, f64)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
@@ -73,6 +74,7 @@ impl Renderer {
|
|||||||
media_stop_icon_surface: None,
|
media_stop_icon_surface: None,
|
||||||
media_play_icon_surface: None,
|
media_play_icon_surface: None,
|
||||||
media_next_icon_surface: None,
|
media_next_icon_surface: None,
|
||||||
|
media_rects: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
renderer.load_icons();
|
renderer.load_icons();
|
||||||
@@ -80,37 +82,294 @@ impl Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn load_icons(&mut self) {
|
fn load_icons(&mut self) {
|
||||||
if let Some(ref path) = self.config.wifi_icon {
|
log::info!("Attempting to load status icons...");
|
||||||
self.wifi_icon_surface = self.load_icon(path);
|
let wifi_names = [
|
||||||
|
"network-wireless-signal-excellent-symbolic",
|
||||||
|
"network-wireless-signal-excellent",
|
||||||
|
"network-wireless-symbolic",
|
||||||
|
"network-wireless",
|
||||||
|
];
|
||||||
|
let wifi_path = self
|
||||||
|
.config
|
||||||
|
.wifi_icon
|
||||||
|
.clone()
|
||||||
|
.or_else(|| {
|
||||||
|
for name in &wifi_names {
|
||||||
|
if let Some(path) = self.find_system_icon(name) {
|
||||||
|
return Some(path);
|
||||||
}
|
}
|
||||||
if let Some(ref path) = self.config.bluetooth_icon {
|
|
||||||
self.bluetooth_icon_surface = self.load_icon(path);
|
|
||||||
}
|
}
|
||||||
if let Some(ref path) = self.config.battery_icon {
|
None
|
||||||
self.battery_icon_surface = self.load_icon(path);
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
if !wifi_path.is_empty() {
|
||||||
|
log::info!("Resolved WiFi icon path: {}", wifi_path);
|
||||||
|
self.wifi_icon_surface = self.load_icon(&wifi_path);
|
||||||
|
} else {
|
||||||
|
log::warn!("WiFi icon not found in system themes.");
|
||||||
}
|
}
|
||||||
if let Some(ref path) = self.config.media_prev_icon {
|
|
||||||
self.media_prev_icon_surface = self.load_icon(path);
|
let bt_names = [
|
||||||
|
"bluetooth-active-symbolic",
|
||||||
|
"bluetooth-symbolic",
|
||||||
|
"bluetooth-active",
|
||||||
|
"bluetooth",
|
||||||
|
];
|
||||||
|
let bt_path = self
|
||||||
|
.config
|
||||||
|
.bluetooth_icon
|
||||||
|
.clone()
|
||||||
|
.or_else(|| {
|
||||||
|
for name in &bt_names {
|
||||||
|
if let Some(path) = self.find_system_icon(name) {
|
||||||
|
return Some(path);
|
||||||
}
|
}
|
||||||
if let Some(ref path) = self.config.media_stop_icon {
|
|
||||||
self.media_stop_icon_surface = self.load_icon(path);
|
|
||||||
}
|
}
|
||||||
if let Some(ref path) = self.config.media_play_icon {
|
None
|
||||||
self.media_play_icon_surface = self.load_icon(path);
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
if !bt_path.is_empty() {
|
||||||
|
log::info!("Resolved Bluetooth icon path: {}", bt_path);
|
||||||
|
self.bluetooth_icon_surface = self.load_icon(&bt_path);
|
||||||
|
} else {
|
||||||
|
log::warn!("Bluetooth icon not found in system themes.");
|
||||||
}
|
}
|
||||||
if let Some(ref path) = self.config.media_next_icon {
|
|
||||||
self.media_next_icon_surface = self.load_icon(path);
|
let batt_names = [
|
||||||
|
"battery-level-100-symbolic",
|
||||||
|
"battery-full-symbolic",
|
||||||
|
"battery-full",
|
||||||
|
"battery-level-100",
|
||||||
|
"battery",
|
||||||
|
"battery-symbolic",
|
||||||
|
];
|
||||||
|
let batt_path = self
|
||||||
|
.config
|
||||||
|
.battery_icon
|
||||||
|
.clone()
|
||||||
|
.or_else(|| {
|
||||||
|
for name in &batt_names {
|
||||||
|
if let Some(path) = self.find_system_icon(name) {
|
||||||
|
return Some(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
if !batt_path.is_empty() {
|
||||||
|
log::info!("Resolved Battery icon path: {}", batt_path);
|
||||||
|
self.battery_icon_surface = self.load_icon(&batt_path);
|
||||||
|
} else {
|
||||||
|
log::warn!("Battery icon not found in system themes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let prev_names = [
|
||||||
|
"media-skip-backward-symbolic",
|
||||||
|
"media-skip-backward",
|
||||||
|
"media-playlist-repeat-symbolic",
|
||||||
|
];
|
||||||
|
let prev_path = self
|
||||||
|
.config
|
||||||
|
.media_prev_icon
|
||||||
|
.clone()
|
||||||
|
.or_else(|| {
|
||||||
|
for name in &prev_names {
|
||||||
|
if let Some(path) = self.find_system_icon(name) {
|
||||||
|
return Some(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
if !prev_path.is_empty() {
|
||||||
|
log::debug!("Resolved Media Prev icon path: {}", prev_path);
|
||||||
|
self.media_prev_icon_surface = self.load_icon(&prev_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let stop_names = ["media-playback-stop-symbolic", "media-playback-stop"];
|
||||||
|
let stop_path = self
|
||||||
|
.config
|
||||||
|
.media_stop_icon
|
||||||
|
.clone()
|
||||||
|
.or_else(|| {
|
||||||
|
for name in &stop_names {
|
||||||
|
if let Some(path) = self.find_system_icon(name) {
|
||||||
|
return Some(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
if !stop_path.is_empty() {
|
||||||
|
log::debug!("Resolved Media Stop icon path: {}", stop_path);
|
||||||
|
self.media_stop_icon_surface = self.load_icon(&stop_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let play_names = ["media-playback-start-symbolic", "media-playback-start"];
|
||||||
|
let play_path = self
|
||||||
|
.config
|
||||||
|
.media_play_icon
|
||||||
|
.clone()
|
||||||
|
.or_else(|| {
|
||||||
|
for name in &play_names {
|
||||||
|
if let Some(path) = self.find_system_icon(name) {
|
||||||
|
return Some(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
if !play_path.is_empty() {
|
||||||
|
log::debug!("Resolved Media Play icon path: {}", play_path);
|
||||||
|
self.media_play_icon_surface = self.load_icon(&play_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let next_names = ["media-skip-forward-symbolic", "media-skip-forward"];
|
||||||
|
let next_path = self
|
||||||
|
.config
|
||||||
|
.media_next_icon
|
||||||
|
.clone()
|
||||||
|
.or_else(|| {
|
||||||
|
for name in &next_names {
|
||||||
|
if let Some(path) = self.find_system_icon(name) {
|
||||||
|
return Some(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
if !next_path.is_empty() {
|
||||||
|
log::debug!("Resolved Media Next icon path: {}", next_path);
|
||||||
|
self.media_next_icon_surface = self.load_icon(&next_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_system_icon(&self, name: &str) -> Option<String> {
|
||||||
|
let data_dirs = std::env::var("XDG_DATA_DIRS").unwrap_or_default();
|
||||||
|
let mut search_paths = Vec::new();
|
||||||
|
|
||||||
|
for dir in data_dirs.split(':') {
|
||||||
|
let p = std::path::PathBuf::from(dir).join("icons");
|
||||||
|
if p.exists() {
|
||||||
|
search_paths.push(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let sys_path = std::path::PathBuf::from("/run/current-system/sw/share/icons");
|
||||||
|
if sys_path.exists() {
|
||||||
|
search_paths.push(sys_path);
|
||||||
|
}
|
||||||
|
let usr_path = std::path::PathBuf::from("/usr/share/icons");
|
||||||
|
if usr_path.exists() {
|
||||||
|
search_paths.push(usr_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let themes = [
|
||||||
|
"WhiteSur",
|
||||||
|
"WhiteSur-dark",
|
||||||
|
"WhiteSur-light",
|
||||||
|
"Adwaita",
|
||||||
|
"hicolor",
|
||||||
|
"breeze",
|
||||||
|
"Papirus",
|
||||||
|
];
|
||||||
|
let categories = [
|
||||||
|
"status/symbolic",
|
||||||
|
"actions/symbolic",
|
||||||
|
"devices/symbolic",
|
||||||
|
"status/24",
|
||||||
|
"status/22",
|
||||||
|
"status/16",
|
||||||
|
"status",
|
||||||
|
"actions",
|
||||||
|
"devices",
|
||||||
|
"symbolic/status",
|
||||||
|
"symbolic/actions",
|
||||||
|
"symbolic/devices",
|
||||||
|
"24x24/status",
|
||||||
|
"22x22/status",
|
||||||
|
"16x16/status",
|
||||||
|
"48x48/status",
|
||||||
|
];
|
||||||
|
|
||||||
|
for base in &search_paths {
|
||||||
|
for theme in &themes {
|
||||||
|
for cat in &categories {
|
||||||
|
for ext in [".svg", ".png"] {
|
||||||
|
let icon_path = base.join(theme).join(cat).join(format!("{}{}", name, ext));
|
||||||
|
if icon_path.exists() {
|
||||||
|
log::debug!("Icon found: {:?}", icon_path);
|
||||||
|
return Some(icon_path.to_string_lossy().into_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for base in &search_paths {
|
||||||
|
for theme in &themes {
|
||||||
|
let theme_root = base.join(theme);
|
||||||
|
if !theme_root.exists() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(entries) = std::fs::read_dir(&theme_root) {
|
||||||
|
for entry in entries.flatten() {
|
||||||
|
if entry.path().is_dir() {
|
||||||
|
for ext in [".svg", ".png"] {
|
||||||
|
let icon_path = entry.path().join(format!("{}{}", name, ext));
|
||||||
|
if icon_path.exists() {
|
||||||
|
log::debug!("Icon found (deep): {:?}", icon_path);
|
||||||
|
return Some(icon_path.to_string_lossy().into_owned());
|
||||||
|
}
|
||||||
|
if let Ok(sub_entries) = std::fs::read_dir(entry.path()) {
|
||||||
|
for sub_entry in sub_entries.flatten() {
|
||||||
|
if sub_entry.path().is_dir() {
|
||||||
|
let icon_path =
|
||||||
|
sub_entry.path().join(format!("{}{}", name, ext));
|
||||||
|
if icon_path.exists() {
|
||||||
|
log::debug!("Icon found (deep 2): {:?}", icon_path);
|
||||||
|
return Some(
|
||||||
|
icon_path.to_string_lossy().into_owned(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn load_icon(&self, identifier: &str) -> Option<ImageSurface> {
|
fn load_icon(&self, identifier: &str) -> Option<ImageSurface> {
|
||||||
let path = if identifier.starts_with('/') {
|
let path = if identifier.starts_with('~') {
|
||||||
std::path::PathBuf::from(identifier)
|
let home = std::env::var("HOME").unwrap_or_default();
|
||||||
|
std::path::PathBuf::from(identifier.replacen('~', &home, 1))
|
||||||
} else {
|
} else {
|
||||||
return None;
|
std::path::PathBuf::from(identifier)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(pixbuf) = gdk_pixbuf::Pixbuf::from_file(&path) {
|
if !path.exists() {
|
||||||
|
log::warn!("Icon path does not exist: {:?}", path);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if path.extension().and_then(|s| s.to_str()) == Some("svg") {
|
||||||
|
match self.load_svg_with_resvg(&path) {
|
||||||
|
Some(surface) => return Some(surface),
|
||||||
|
None => log::warn!("resvg failed to load {:?}, falling back to pixbuf", path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match gdk_pixbuf::Pixbuf::from_file(&path) {
|
||||||
|
Ok(pixbuf) => {
|
||||||
let w = pixbuf.width();
|
let w = pixbuf.width();
|
||||||
let h = pixbuf.height();
|
let h = pixbuf.height();
|
||||||
let mut surface = ImageSurface::create(Format::ARgb32, w, h).ok()?;
|
let mut surface = ImageSurface::create(Format::ARgb32, w, h).ok()?;
|
||||||
@@ -120,6 +379,14 @@ impl Renderer {
|
|||||||
let n_channels = pixbuf.n_channels();
|
let n_channels = pixbuf.n_channels();
|
||||||
let rowstride = pixbuf.rowstride() as usize;
|
let rowstride = pixbuf.rowstride() as usize;
|
||||||
|
|
||||||
|
log::debug!(
|
||||||
|
"Loaded pixbuf from {:?}: {}x{}, {} channels",
|
||||||
|
path,
|
||||||
|
w,
|
||||||
|
h,
|
||||||
|
n_channels
|
||||||
|
);
|
||||||
|
|
||||||
for y in 0..h as usize {
|
for y in 0..h as usize {
|
||||||
for x in 0..w as usize {
|
for x in 0..w as usize {
|
||||||
let pix_idx = y * rowstride + x * n_channels as usize;
|
let pix_idx = y * rowstride + x * n_channels as usize;
|
||||||
@@ -140,10 +407,50 @@ impl Renderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(surface)
|
Some(surface)
|
||||||
} else {
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to load icon from {:?}: {}", path, e);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_svg_with_resvg(&self, path: &std::path::Path) -> Option<ImageSurface> {
|
||||||
|
use resvg::usvg;
|
||||||
|
|
||||||
|
let opt = usvg::Options::default();
|
||||||
|
let svg_data = std::fs::read(path).ok()?;
|
||||||
|
let tree = usvg::Tree::from_data(&svg_data, &opt).ok()?;
|
||||||
|
|
||||||
|
let size = tree.size().to_int_size();
|
||||||
|
let mut pixmap = resvg::tiny_skia::Pixmap::new(size.width(), size.height())?;
|
||||||
|
|
||||||
|
resvg::render(
|
||||||
|
&tree,
|
||||||
|
resvg::tiny_skia::Transform::default(),
|
||||||
|
&mut pixmap.as_mut(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut surface =
|
||||||
|
ImageSurface::create(Format::ARgb32, size.width() as i32, size.height() as i32).ok()?;
|
||||||
|
{
|
||||||
|
let mut surface_data = surface.data().ok()?;
|
||||||
|
let pix_data = pixmap.data();
|
||||||
|
|
||||||
|
for i in (0..pix_data.len()).step_by(4) {
|
||||||
|
let r = pix_data[i];
|
||||||
|
let g = pix_data[i + 1];
|
||||||
|
let b = pix_data[i + 2];
|
||||||
|
let a = pix_data[i + 3];
|
||||||
|
|
||||||
|
surface_data[i] = b;
|
||||||
|
surface_data[i + 1] = g;
|
||||||
|
surface_data[i + 2] = r;
|
||||||
|
surface_data[i + 3] = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(surface)
|
||||||
|
}
|
||||||
|
|
||||||
/// Resize the renderer to new dimensions
|
/// Resize the renderer to new dimensions
|
||||||
pub fn resize(&mut self, width: i32, height: i32) {
|
pub fn resize(&mut self, width: i32, height: i32) {
|
||||||
@@ -199,6 +506,7 @@ impl Renderer {
|
|||||||
|
|
||||||
/// Render the current frame
|
/// Render the current frame
|
||||||
pub fn render(&mut self) {
|
pub fn render(&mut self) {
|
||||||
|
self.media_rects.clear();
|
||||||
// Clear the surface
|
// Clear the surface
|
||||||
self.context.new_path();
|
self.context.new_path();
|
||||||
self.context.set_source_rgba(0.0, 0.0, 0.0, 1.0);
|
self.context.set_source_rgba(0.0, 0.0, 0.0, 1.0);
|
||||||
@@ -652,13 +960,17 @@ impl Renderer {
|
|||||||
|
|
||||||
let status_text = if self.system_status.media_playing {
|
let status_text = if self.system_status.media_playing {
|
||||||
if let Some(ref icon) = self.media_play_icon_surface {
|
if let Some(ref icon) = self.media_play_icon_surface {
|
||||||
// Draw play icon instead of text
|
|
||||||
let play_y = start_y + 40.0;
|
let play_y = start_y + 40.0;
|
||||||
self.draw_icon_at(
|
let rx = center_x - icon.width() as f64 / 2.0;
|
||||||
center_x - icon.width() as f64 / 2.0,
|
let ry = play_y - icon.height() as f64 / 2.0;
|
||||||
play_y - icon.height() as f64 / 2.0,
|
self.draw_icon_at(rx, ry, icon);
|
||||||
icon,
|
self.media_rects.push((
|
||||||
);
|
"play".to_string(),
|
||||||
|
rx,
|
||||||
|
ry,
|
||||||
|
icon.width() as f64,
|
||||||
|
icon.height() as f64,
|
||||||
|
));
|
||||||
""
|
""
|
||||||
} else {
|
} else {
|
||||||
"▶ Playing"
|
"▶ Playing"
|
||||||
@@ -685,7 +997,11 @@ impl Renderer {
|
|||||||
// Previous icon
|
// Previous icon
|
||||||
if let Some(ref icon) = self.media_prev_icon_surface {
|
if let Some(ref icon) = self.media_prev_icon_surface {
|
||||||
let ix = controls_center_x - icon_spacing;
|
let ix = controls_center_x - icon_spacing;
|
||||||
self.draw_icon_at(ix - icon_size / 2.0, controls_y - icon_size / 2.0, icon);
|
let rx = ix - icon_size / 2.0;
|
||||||
|
let ry = controls_y - icon_size / 2.0;
|
||||||
|
self.draw_icon_at(rx, ry, icon);
|
||||||
|
self.media_rects
|
||||||
|
.push(("prev".to_string(), rx, ry, icon_size, icon_size));
|
||||||
} else {
|
} else {
|
||||||
self.context
|
self.context
|
||||||
.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.7);
|
.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.7);
|
||||||
@@ -698,7 +1014,11 @@ impl Renderer {
|
|||||||
// Stop icon
|
// Stop icon
|
||||||
if let Some(ref icon) = self.media_stop_icon_surface {
|
if let Some(ref icon) = self.media_stop_icon_surface {
|
||||||
let ix = controls_center_x;
|
let ix = controls_center_x;
|
||||||
self.draw_icon_at(ix - icon_size / 2.0, controls_y - icon_size / 2.0, icon);
|
let rx = ix - icon_size / 2.0;
|
||||||
|
let ry = controls_y - icon_size / 2.0;
|
||||||
|
self.draw_icon_at(rx, ry, icon);
|
||||||
|
self.media_rects
|
||||||
|
.push(("stop".to_string(), rx, ry, icon_size, icon_size));
|
||||||
} else {
|
} else {
|
||||||
self.context
|
self.context
|
||||||
.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.7);
|
.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.7);
|
||||||
@@ -710,7 +1030,11 @@ impl Renderer {
|
|||||||
// Next icon
|
// Next icon
|
||||||
if let Some(ref icon) = self.media_next_icon_surface {
|
if let Some(ref icon) = self.media_next_icon_surface {
|
||||||
let ix = controls_center_x + icon_spacing;
|
let ix = controls_center_x + icon_spacing;
|
||||||
self.draw_icon_at(ix - icon_size / 2.0, controls_y - icon_size / 2.0, icon);
|
let rx = ix - icon_size / 2.0;
|
||||||
|
let ry = controls_y - icon_size / 2.0;
|
||||||
|
self.draw_icon_at(rx, ry, icon);
|
||||||
|
self.media_rects
|
||||||
|
.push(("next".to_string(), rx, ry, icon_size, icon_size));
|
||||||
} else {
|
} else {
|
||||||
self.context
|
self.context
|
||||||
.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.7);
|
.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.7);
|
||||||
@@ -733,7 +1057,7 @@ impl Renderer {
|
|||||||
if let Some(ref ssid) = self.system_status.wifi_ssid {
|
if let Some(ref ssid) = self.system_status.wifi_ssid {
|
||||||
if let Some(ref icon) = self.wifi_icon_surface {
|
if let Some(ref icon) = self.wifi_icon_surface {
|
||||||
self.draw_icon_at(x, y - 15.0, icon);
|
self.draw_icon_at(x, y - 15.0, icon);
|
||||||
let text_x = x + icon.width() as f64 + 10.0;
|
let text_x = x + 24.0 + 10.0;
|
||||||
self.context.new_path();
|
self.context.new_path();
|
||||||
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha);
|
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha);
|
||||||
self.context.set_font_size(16.0);
|
self.context.set_font_size(16.0);
|
||||||
@@ -766,7 +1090,7 @@ impl Renderer {
|
|||||||
|
|
||||||
if let Some(ref icon) = self.battery_icon_surface {
|
if let Some(ref icon) = self.battery_icon_surface {
|
||||||
self.draw_icon_at(x, y - 15.0, icon);
|
self.draw_icon_at(x, y - 15.0, icon);
|
||||||
let text_x = x + icon.width() as f64 + 10.0;
|
let text_x = x + 24.0 + 10.0;
|
||||||
let battery_text = format!("{:.0}%", percent);
|
let battery_text = format!("{:.0}%", percent);
|
||||||
self.context.new_path();
|
self.context.new_path();
|
||||||
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha);
|
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha);
|
||||||
@@ -800,35 +1124,37 @@ impl Renderer {
|
|||||||
let x = margin;
|
let x = margin;
|
||||||
let y = margin + 50.0;
|
let y = margin + 50.0;
|
||||||
|
|
||||||
|
let status_text;
|
||||||
|
let is_off;
|
||||||
|
|
||||||
if self.system_status.bluetooth_connected {
|
if self.system_status.bluetooth_connected {
|
||||||
if let Some(ref icon) = self.bluetooth_icon_surface {
|
status_text = self.system_status.bluetooth_devices.join(", ");
|
||||||
self.draw_icon_at(x, y - 12.0, icon);
|
is_off = false;
|
||||||
let text_x = x + icon.width() as f64 + 10.0;
|
|
||||||
let devices = self.system_status.bluetooth_devices.join(", ");
|
|
||||||
self.context.new_path();
|
|
||||||
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha);
|
|
||||||
self.context.set_font_size(14.0);
|
|
||||||
self.context.move_to(text_x, y);
|
|
||||||
self.context.show_text(&devices).unwrap();
|
|
||||||
} else {
|
} else {
|
||||||
let text = format!(
|
status_text = "Bluetooth off".to_string();
|
||||||
"🔵 {} device(s)",
|
is_off = true;
|
||||||
self.system_status.bluetooth_devices.len()
|
|
||||||
);
|
|
||||||
self.context.new_path();
|
|
||||||
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha);
|
|
||||||
self.context.set_font_size(14.0);
|
|
||||||
self.context.move_to(x, y);
|
|
||||||
self.context.show_text(&text).unwrap();
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
let text = "🔴 Bluetooth off";
|
let alpha_mult = if is_off { 0.5 } else { 1.0 };
|
||||||
|
|
||||||
|
if let Some(ref icon) = self.bluetooth_icon_surface {
|
||||||
|
self.draw_icon_at_with_alpha(x, y - 12.0, icon, alpha_mult);
|
||||||
|
let text_x = x + 24.0 + 10.0;
|
||||||
self.context.new_path();
|
self.context.new_path();
|
||||||
self.context
|
self.context
|
||||||
.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.5);
|
.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * alpha_mult);
|
||||||
|
self.context.set_font_size(14.0);
|
||||||
|
self.context.move_to(text_x, y);
|
||||||
|
self.context.show_text(&status_text).unwrap();
|
||||||
|
} else {
|
||||||
|
let emoji = if is_off { "🔴 " } else { "🔵 " };
|
||||||
|
let full_text = format!("{}{}", emoji, status_text);
|
||||||
|
self.context.new_path();
|
||||||
|
self.context
|
||||||
|
.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * alpha_mult);
|
||||||
self.context.set_font_size(14.0);
|
self.context.set_font_size(14.0);
|
||||||
self.context.move_to(x, y);
|
self.context.move_to(x, y);
|
||||||
self.context.show_text(text).unwrap();
|
self.context.show_text(&full_text).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -862,6 +1188,21 @@ impl Renderer {
|
|||||||
self.context.restore().unwrap();
|
self.context.restore().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn draw_icon_at_with_alpha(&self, x: f64, y: f64, surface: &ImageSurface, alpha: f64) {
|
||||||
|
self.context.save().unwrap();
|
||||||
|
let target_size = 24.0;
|
||||||
|
let scale_x = target_size / surface.width() as f64;
|
||||||
|
let scale_y = target_size / surface.height() as f64;
|
||||||
|
let scale = scale_x.min(scale_y);
|
||||||
|
self.context.translate(x, y);
|
||||||
|
self.context.scale(scale, scale);
|
||||||
|
self.context.set_source_surface(surface, 0.0, 0.0).unwrap();
|
||||||
|
self.context
|
||||||
|
.paint_with_alpha(self.fade_alpha * alpha)
|
||||||
|
.unwrap();
|
||||||
|
self.context.restore().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
fn draw_battery_icon_at(
|
fn draw_battery_icon_at(
|
||||||
&self,
|
&self,
|
||||||
x: f64,
|
x: f64,
|
||||||
|
|||||||
@@ -259,6 +259,14 @@ impl SystemManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn media_stop(&self) {
|
||||||
|
if let Ok(finder) = PlayerFinder::new() {
|
||||||
|
if let Ok(player) = finder.find_active() {
|
||||||
|
let _ = player.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn media_next(&self) {
|
pub fn media_next(&self) {
|
||||||
if let Ok(finder) = PlayerFinder::new() {
|
if let Ok(finder) = PlayerFinder::new() {
|
||||||
if let Ok(player) = finder.find_active() {
|
if let Ok(player) = finder.find_active() {
|
||||||
|
|||||||
Reference in New Issue
Block a user