diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c5e7668..77de25a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,9 +4,6 @@ on: push: tags: ["v*"] -permissions: - contents: write - env: DL_DIR: /srv/breadway-dl ECOSYSTEM_DIR: /home/breadway/Projects/bread-ecosystem @@ -17,8 +14,8 @@ jobs: steps: - uses: actions/checkout@v4 - - name: install build deps - run: sudo apt-get install -y libgtk-4-dev libdbus-1-dev pkg-config iw 2>/dev/null || true + - name: install system deps + run: sudo pacman -S --noconfirm gtk4 gtk4-layer-shell iw 2>/dev/null || true - name: build run: cargo build --release --locked @@ -33,7 +30,7 @@ jobs: sha256sum "${PKG_DIR}/breadbar-x86_64" | awk '{print $1}' \ > "${PKG_DIR}/breadbar-x86_64.sha256" cp bakery.toml "${PKG_DIR}/bakery.toml" - ln -sfn "${VERSION}" "${DL_DIR}/breadbar/latest" + ln -sfn "${PKG_DIR}" "${DL_DIR}/breadbar/latest" - name: ensure bread-ecosystem run: | @@ -53,8 +50,6 @@ jobs: run: | VERSION="${GITHUB_REF_NAME#v}" PKG_DIR="${DL_DIR}/breadbar/${VERSION}" - gh release create "${GITHUB_REF_NAME}" \ - --title "breadbar v${VERSION}" --generate-notes 2>/dev/null || true gh release upload "${GITHUB_REF_NAME}" \ "${PKG_DIR}/breadbar-x86_64" \ "${PKG_DIR}/breadbar-x86_64.sha256" \ diff --git a/Cargo.lock b/Cargo.lock index dd6d212..b3b2530 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,20 +66,19 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.5.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "bread-theme" version = "0.1.0" -source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.1.0#6b5f4f475f66a645b08cb865e6dda8228d23679b" dependencies = [ "dirs", "gtk4", @@ -89,7 +88,7 @@ dependencies = [ [[package]] name = "breadbar" -version = "0.1.2" +version = "0.1.0" dependencies = [ "bread-theme", "futures-lite", @@ -105,9 +104,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.20.3" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytes" @@ -140,9 +139,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.20.8" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb693542bcafa528e198be0ebd9d3632ca5b7c93dbe7237460e199910835997c" +checksum = "3c6b04e07d8080154ed4ac03546d9a2b303cc2fe1901ba0b35b301516e289368" dependencies = [ "smallvec", "target-lexicon", @@ -224,9 +223,9 @@ dependencies = [ [[package]] name = "either" -version = "1.16.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "endi" @@ -855,9 +854,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.99" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" dependencies = [ "cfg-if", "futures-util", @@ -909,15 +908,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.32" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" -version = "2.8.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memoffset" @@ -930,9 +929,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi", @@ -1175,9 +1174,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", @@ -1230,9 +1229,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.4" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", "windows-sys 0.61.2", @@ -1273,9 +1272,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.13.5" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" +checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" [[package]] name = "tempfile" @@ -1364,9 +1363,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.12+spec-1.1.0" +version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ "indexmap", "toml_datetime", @@ -1439,9 +1438,9 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" -version = "1.13.3" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-xid" @@ -1451,9 +1450,9 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "uuid" -version = "1.23.2" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ "js-sys", "serde_core", @@ -1492,9 +1491,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.122" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" dependencies = [ "cfg-if", "once_cell", @@ -1505,9 +1504,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.122" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1515,9 +1514,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.122" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" dependencies = [ "bumpalo", "proc-macro2", @@ -1528,9 +1527,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.122" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" dependencies = [ "unicode-ident", ] @@ -1761,9 +1760,9 @@ checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" [[package]] name = "zbus" -version = "5.16.0" +version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285" +checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1" dependencies = [ "async-broadcast", "async-recursion", @@ -1791,9 +1790,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.16.0" +version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6" +checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1823,9 +1822,9 @@ checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zvariant" -version = "5.12.0" +version = "5.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0" +checksum = "1c1567a6ec68df868cbbfde844cfc6d81649fe5109a62b116b19fabd53e618ee" dependencies = [ "endi", "enumflags2", @@ -1837,9 +1836,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "5.12.0" +version = "5.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737" +checksum = "c7d5b780599bbde114e39d9a0799577fad1ced5105d38515745f7b3099d8ceda" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1850,9 +1849,9 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "3.4.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6" +checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 4594c93..30b6e81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "breadbar" -version = "0.1.2" +version = "0.1.0" edition = "2021" description = "Minimal status bar and notification daemon for Hyprland on Wayland" license = "MIT" @@ -10,7 +10,9 @@ keywords = ["wayland", "hyprland", "bar", "status-bar", "gtk4"] categories = ["gui"] [dependencies] -bread-theme = { git = "https://github.com/Breadway/bread-ecosystem", tag = "v0.1.0", features = ["gtk"] } +# Path dep for local dev; replace with git dep on first tag: +# bread-theme = { git = "https://github.com/Breadway/bread-ecosystem", tag = "theme-v0.1.0", features = ["gtk"] } +bread-theme = { path = "../bread-ecosystem/bread-theme", features = ["gtk"] } gtk4 = { version = "0.11", features = ["v4_12"] } gtk4-layer-shell = "0.8" relm4 = { version = "0.11", features = ["macros"] } diff --git a/README.md b/README.md index c823661..1e577e8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Minimal status bar and notification daemon for [Hyprland](https://hyprland.org/) on Wayland. -A single Rust binary that provides a full-width top bar, a system tray, and a standards-compliant D-Bus notification daemon. No launcher, no wallpaper logic. +A single Rust binary that provides a full-width top bar and a standards-compliant D-Bus notification daemon, with no system tray, no launcher, and no wallpaper logic. ## Features @@ -10,7 +10,7 @@ A single Rust binary that provides a full-width top bar, a system tray, and a st - Left: live workspace buttons sourced from Hyprland IPC, active workspace highlighted - Centre: clock (`HH:MM`, updates at the top of each minute) -- Right: CPU%, RAM, power draw (W), battery level + AC indicator, Bluetooth state, WiFi SSID with signal strength, system tray (SNI) +- Right: CPU%, RAM, power draw (W), battery level + AC indicator, Bluetooth state, WiFi SSID with signal strength **Notification daemon**: @@ -105,7 +105,6 @@ Example — change the font size: | `src/bar/workspaces.rs` | Hyprland IPC event stream, workspace buttons | | `src/bar/clock.rs` | Minute-tick clock | | `src/bar/stats.rs` | Polling loop: CPU, RAM, power, battery, Bluetooth, WiFi | -| `src/bar/tray.rs` | `org.kde.StatusNotifierWatcher` D-Bus service, SNI item rendering | | `src/notifications/mod.rs` | `org.freedesktop.Notifications` zbus service | | `src/notifications/popup.rs` | Layer-shell popup window and card stack | | `src/theme.rs` | pywal reader, GTK CSS provider injection | diff --git a/bakery.toml b/bakery.toml index 03f03fe..a334ea4 100644 --- a/bakery.toml +++ b/bakery.toml @@ -1,8 +1,7 @@ name = "breadbar" description = "Minimal status bar and notification daemon for Hyprland" binaries = ["breadbar"] -system_deps = ["gtk4", "gtk4-layer-shell", "iw", "libpulse"] -optional_system_deps = ["hyprland"] +system_deps = ["gtk4", "gtk4-layer-shell", "iw"] bread_deps = [] [config] diff --git a/src/bar/mod.rs b/src/bar/mod.rs index 7b0d41d..24c75b5 100644 --- a/src/bar/mod.rs +++ b/src/bar/mod.rs @@ -1,4 +1,3 @@ pub mod clock; pub mod stats; -pub mod tray; pub mod workspaces; diff --git a/src/bar/tray.rs b/src/bar/tray.rs deleted file mode 100644 index 421b7c8..0000000 --- a/src/bar/tray.rs +++ /dev/null @@ -1,240 +0,0 @@ -use crate::{App, AppInput}; -use futures_lite::StreamExt; -use gtk4::prelude::Cast; -use relm4::ComponentSender; -use std::collections::HashMap; -use std::sync::{Arc, Mutex}; -use zbus::{interface, object_server::SignalEmitter}; - -#[derive(Debug)] -pub enum TrayIconData { - Pixels { width: i32, height: i32, data: Vec }, - Name(String), -} - -#[derive(Debug)] -pub enum TrayUpdate { - Add { id: String, icon: Option, title: String }, - Remove { id: String }, -} - -struct WatcherState { - items: Vec, -} - -struct Watcher { - state: Arc>, - tx: tokio::sync::mpsc::UnboundedSender<(String, String)>, -} - -#[interface(name = "org.kde.StatusNotifierWatcher")] -impl Watcher { - async fn register_status_notifier_item( - &self, - service: String, - #[zbus(header)] header: zbus::message::Header<'_>, - #[zbus(signal_emitter)] ctx: SignalEmitter<'_>, - ) { - let sender_name = header.sender().map(|s| s.to_string()).unwrap_or_default(); - let (bus, path) = parse_service(&service, &sender_name); - let full = format!("{}{}", bus, path); - { - let mut state = self.state.lock().unwrap(); - if !state.items.contains(&full) { - state.items.push(full.clone()); - } - } - let _ = Self::status_notifier_item_registered(&ctx, &full).await; - let _ = self.tx.send((bus, path)); - } - - async fn register_status_notifier_host( - &self, - _service: String, - #[zbus(signal_emitter)] ctx: SignalEmitter<'_>, - ) { - let _ = Self::status_notifier_host_registered(&ctx).await; - } - - #[zbus(property)] - fn registered_status_notifier_items(&self) -> Vec { - self.state.lock().unwrap().items.clone() - } - - #[zbus(property)] - fn is_status_notifier_host_registered(&self) -> bool { - true - } - - #[zbus(property)] - fn protocol_version(&self) -> i32 { - 0 - } - - #[zbus(signal)] - async fn status_notifier_item_registered( - ctx: &SignalEmitter<'_>, - service: &str, - ) -> zbus::Result<()>; - - #[zbus(signal)] - async fn status_notifier_item_unregistered( - ctx: &SignalEmitter<'_>, - service: &str, - ) -> zbus::Result<()>; - - #[zbus(signal)] - async fn status_notifier_host_registered(ctx: &SignalEmitter<'_>) -> zbus::Result<()>; -} - -fn parse_service(service: &str, sender: &str) -> (String, String) { - if service.starts_with('/') { - return (sender.to_string(), service.to_string()); - } - match service.find('/') { - Some(slash) => (service[..slash].to_string(), service[slash..].to_string()), - None => (service.to_string(), "/StatusNotifierItem".to_string()), - } -} - -async fn read_item( - conn: &zbus::Connection, - bus: &str, - path: &str, -) -> (Option, String) { - let Ok(proxy) = zbus::Proxy::new(conn, bus, path, "org.kde.StatusNotifierItem").await else { - return (None, String::new()); - }; - let icon = read_icon(&proxy).await; - let title = proxy.get_property::("Title").await.unwrap_or_default(); - (icon, title) -} - -async fn read_icon(proxy: &zbus::Proxy<'_>) -> Option { - let pixmaps: Vec<(i32, i32, Vec)> = - proxy.get_property("IconPixmap").await.unwrap_or_default(); - - if !pixmaps.is_empty() { - return pixmaps - .into_iter() - .filter(|(w, h, _)| *w > 0 && *h > 0) - .min_by_key(|(w, h, _)| (w.max(h) - 22).abs()) - .map(|(width, height, data)| TrayIconData::Pixels { width, height, data }); - } - - let name: String = proxy.get_property("IconName").await.ok()?; - if name.is_empty() { - return None; - } - Some(TrayIconData::Name(name)) -} - -/// Call `Activate(0, 0)` on the SNI item identified by `id` (`{bus}{path}`). -pub fn spawn_activate(id: String) { - relm4::spawn(async move { - let (bus, path) = match id.find('/') { - Some(slash) => (id[..slash].to_string(), id[slash..].to_string()), - None => (id, "/StatusNotifierItem".to_string()), - }; - let Ok(conn) = zbus::Connection::session().await else { return }; - let Ok(proxy) = zbus::Proxy::new(&conn, bus.as_str(), path.as_str(), "org.kde.StatusNotifierItem").await else { return }; - let _ = proxy.call_method("Activate", &(0i32, 0i32)).await; - }); -} - -pub fn spawn_watcher(sender: ComponentSender) { - let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<(String, String)>(); - // Maps bus name → item ids, shared between registration and cleanup tasks. - let bus_map: Arc>>> = Arc::new(Mutex::new(HashMap::new())); - let bus_map_cleanup = bus_map.clone(); - let sender_cleanup = sender.clone(); - - // Registration task — owns the watcher service and processes new items. - relm4::spawn(async move { - let watcher = Watcher { - state: Arc::new(Mutex::new(WatcherState { items: Vec::new() })), - tx, - }; - // Builder steps fail only on invalid static strings — safe to unwrap. - let conn = zbus::connection::Builder::session() - .unwrap() - .name("org.kde.StatusNotifierWatcher") - .unwrap() - .serve_at("/StatusNotifierWatcher", watcher) - .unwrap() - .build() - .await - .expect("failed to register org.kde.StatusNotifierWatcher"); - - while let Some((bus, path)) = rx.recv().await { - let (icon, title) = read_item(&conn, &bus, &path).await; - let id = format!("{}{}", bus, path); - bus_map.lock().unwrap().entry(bus).or_default().push(id.clone()); - sender.input(AppInput::TrayUpdate(TrayUpdate::Add { id, icon, title })); - } - }); - - // Cleanup task — watches NameOwnerChanged and removes items when their owner exits. - relm4::spawn(async move { - let Ok(conn) = zbus::Connection::session().await else { return }; - let Ok(proxy) = zbus::fdo::DBusProxy::new(&conn).await else { return }; - let Ok(mut stream) = proxy.receive_name_owner_changed().await else { return }; - while let Some(signal) = stream.next().await { - let Ok(args) = signal.args() else { continue }; - if args.new_owner().is_none() { - let gone = args.name().to_string(); - if let Some(ids) = bus_map_cleanup.lock().unwrap().remove(&gone) { - for id in ids { - sender_cleanup.input(AppInput::TrayUpdate(TrayUpdate::Remove { id })); - } - } - } - } - }); -} - -/// Convert SNI ARGB pixel data (network byte order) to a GTK4 `Image`. -/// Falls back to an icon-name lookup or a placeholder on failure. -pub fn make_tray_image(icon: Option<&TrayIconData>) -> gtk4::Image { - let img = match icon { - Some(TrayIconData::Pixels { width, height, data }) => pixels_to_image(*width, *height, data), - Some(TrayIconData::Name(name)) => { - let img = gtk4::Image::from_icon_name(name); - img.set_pixel_size(16); - Some(img) - } - None => None, - }; - img.unwrap_or_else(|| { - let img = gtk4::Image::from_icon_name("image-missing"); - img.set_pixel_size(16); - img - }) -} - -fn pixels_to_image(width: i32, height: i32, data: &[u8]) -> Option { - if data.len() != (width * height * 4) as usize { - return None; - } - // SNI delivers ARGB big-endian: bytes are [A, R, G, B] per pixel. - // GTK4 R8g8b8a8 expects [R, G, B, A] per pixel. - let mut rgba = Vec::with_capacity(data.len()); - for chunk in data.chunks_exact(4) { - rgba.push(chunk[1]); - rgba.push(chunk[2]); - rgba.push(chunk[3]); - rgba.push(chunk[0]); - } - let bytes = gtk4::glib::Bytes::from_owned(rgba); - let tex = gtk4::gdk::MemoryTexture::new( - width, - height, - gtk4::gdk::MemoryFormat::R8g8b8a8, - &bytes, - (width * 4) as usize, - ); - let tex: gtk4::gdk::Texture = tex.upcast(); - let img = gtk4::Image::from_paintable(Some(&tex)); - img.set_pixel_size(16); - Some(img) -} diff --git a/src/main.rs b/src/main.rs index f1d77b3..10cca56 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,8 +34,6 @@ pub struct App { wifi_img: gtk4::Image, // Pre-loaded textures indexed by constant pointer values. wifi_textures: std::collections::HashMap, - tray_box: gtk4::Box, - tray_items: std::collections::HashMap, } #[derive(Debug)] @@ -44,7 +42,6 @@ pub enum AppInput { ActiveWorkspace(WorkspaceId), ClockTick, StatsUpdate(bar::stats::Stats), - TrayUpdate(bar::tray::TrayUpdate), } #[relm4::component(pub)] @@ -156,8 +153,6 @@ impl SimpleComponent for App { wifi_lbl: wifi_lbl.clone(), wifi_img: wifi_img.clone(), wifi_textures, - tray_box: gtk4::Box::new(gtk4::Orientation::Horizontal, 4), - tray_items: std::collections::HashMap::new(), }; let widgets = view_output!(); model.workspace_box = widgets.workspace_box.clone(); @@ -184,15 +179,12 @@ impl SimpleComponent for App { wifi_pair.append(&wifi_img); wifi_pair.append(&wifi_lbl); stats_box.append(&wifi_pair); - model.tray_box.add_css_class("tray-box"); - stats_box.append(&model.tray_box); widgets.center_box.set_end_widget(Some(&stats_box)); theme::apply(); bar::workspaces::spawn_watcher(sender.clone()); bar::clock::spawn_ticker(sender.clone()); - bar::stats::spawn_poller(sender.clone()); - bar::tray::spawn_watcher(sender.clone()); + bar::stats::spawn_poller(sender); notifications::spawn(); osd::spawn(); @@ -236,26 +228,6 @@ impl SimpleComponent for App { self.wifi_img.set_paintable(Some(tex)); } } - AppInput::TrayUpdate(bar::tray::TrayUpdate::Add { id, icon, title }) => { - if self.tray_items.contains_key(&id) { - return; - } - let btn = gtk4::Button::new(); - btn.add_css_class("tray-btn"); - btn.set_child(Some(&bar::tray::make_tray_image(icon.as_ref()))); - if !title.is_empty() { - btn.set_tooltip_text(Some(&title)); - } - let id_click = id.clone(); - btn.connect_clicked(move |_| bar::tray::spawn_activate(id_click.clone())); - self.tray_box.append(&btn); - self.tray_items.insert(id, btn); - } - AppInput::TrayUpdate(bar::tray::TrayUpdate::Remove { id }) => { - if let Some(btn) = self.tray_items.remove(&id) { - self.tray_box.remove(&btn); - } - } } } } diff --git a/src/notifications/mod.rs b/src/notifications/mod.rs index b6c60b2..b197038 100644 --- a/src/notifications/mod.rs +++ b/src/notifications/mod.rs @@ -70,7 +70,7 @@ impl NotifServer { ( "breadbar".into(), "breadway".into(), - env!("CARGO_PKG_VERSION").into(), + "0.1.0".into(), "1.2".into(), ) } diff --git a/src/osd.rs b/src/osd.rs index 74f0ec7..61f7ea4 100644 --- a/src/osd.rs +++ b/src/osd.rs @@ -35,7 +35,7 @@ fn volume_watcher(tx: mpsc::Sender) { let stdout = child.stdout.take().unwrap(); let reader = BufReader::new(stdout); - for line in reader.lines().map_while(Result::ok) { + for line in reader.lines().flatten() { if line.contains("'change' on sink") { if let Some(evt) = query_volume() { let _ = tx.blocking_send(evt);