diff --git a/Cargo.lock b/Cargo.lock index b748b8bf..c363ddb6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -303,6 +303,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -432,12 +438,29 @@ dependencies = [ "syn 2.0.68", ] +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + [[package]] name = "arc-swap" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + [[package]] name = "ark-bls12-377" version = "0.4.0" @@ -1122,6 +1145,29 @@ dependencies = [ "cc", ] +[[package]] +name = "av1-grain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" +dependencies = [ + "arrayvec", +] + [[package]] name = "backoff" version = "0.4.0" @@ -1253,6 +1299,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitcoin-internals" version = "0.2.0" @@ -1281,6 +1333,12 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "bitstream-io" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcde5f311c85b8ca30c2e4198d4326bc342c76541590106f5fa4a50946ea499" + [[package]] name = "bitvec" version = "1.0.1" @@ -1474,6 +1532,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "built" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "236e6289eda5a812bc6b53c3b024039382a2895fbbeef2d748b2931546d392c4" + [[package]] name = "bumpalo" version = "3.16.0" @@ -1504,6 +1568,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.6.0" @@ -1863,6 +1933,12 @@ dependencies = [ "objc", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "colorchoice" version = "1.0.1" @@ -3074,6 +3150,22 @@ dependencies = [ "syn 2.0.68", ] +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -3902,6 +3994,16 @@ dependencies = [ "polyval", ] +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" version = "0.27.3" @@ -4776,10 +4878,37 @@ checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11" dependencies = [ "bytemuck", "byteorder", + "color_quant", + "exr", + "gif", + "image-webp", "num-traits", "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904" +dependencies = [ + "byteorder-lite", + "quick-error 2.0.1", ] +[[package]] +name = "imgref" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" + [[package]] name = "impl-codec" version = "0.6.0" @@ -4886,6 +5015,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + [[package]] name = "intl-memoizer" version = "0.5.2" @@ -5043,6 +5183,12 @@ dependencies = [ "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + [[package]] name = "js-sys" version = "0.3.69" @@ -5289,12 +5435,29 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + [[package]] name = "libm" version = "0.2.8" @@ -6392,6 +6555,15 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + [[package]] name = "lru" version = "0.10.1" @@ -6439,6 +6611,19 @@ dependencies = [ "libc", ] +[[package]] +name = "mac-notification-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51fca4d74ff9dbaac16a01b924bc3693fa2bba0862c2c633abc73f9a8ea21f64" +dependencies = [ + "cc", + "dirs-next", + "objc-foundation", + "objc_id", + "time", +] + [[package]] name = "mach" version = "0.3.2" @@ -6536,6 +6721,16 @@ dependencies = [ "rawpointer", ] +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + [[package]] name = "memchr" version = "2.7.4" @@ -7059,6 +7254,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nix" version = "0.24.3" @@ -7132,12 +7333,33 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + [[package]] name = "normalize-line-endings" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "notify-rust" +version = "4.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26a1d03b6305ecefdd9c6c60150179bb8d9f0cd4e64bbcad1e41419e7bf5e414" +dependencies = [ + "image", + "lazy_static", + "log", + "mac-notification-sys", + "serde", + "tauri-winrt-notification", + "zbus", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -7182,6 +7404,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + [[package]] name = "num-format" version = "0.4.4" @@ -7207,6 +7440,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ + "num-bigint", "num-integer", "num-traits", ] @@ -8109,6 +8343,25 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "profiling" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" +dependencies = [ + "quote", + "syn 2.0.68", +] + [[package]] name = "prometheus" version = "0.13.4" @@ -8274,6 +8527,15 @@ dependencies = [ "cc", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "quanta" version = "0.12.3" @@ -8295,6 +8557,12 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quick-protobuf" version = "0.8.1" @@ -8566,6 +8834,56 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools 0.12.1", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand 0.8.5", + "rand_chacha 0.3.1", + "simd_helpers", + "system-deps", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67376f469e7e7840d0040bbf4b9b3334005bb167f814621326e4c7ab8cd6e944" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error 2.0.1", + "rav1e", + "rayon", + "rgb", +] + [[package]] name = "raw-cpuid" version = "11.0.2" @@ -8849,7 +9167,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" dependencies = [ "hostname", - "quick-error", + "quick-error 1.2.3", ] [[package]] @@ -8862,6 +9180,15 @@ dependencies = [ "subtle 2.6.1", ] +[[package]] +name = "rgb" +version = "0.8.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.1.0" @@ -10658,6 +10985,15 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + [[package]] name = "simple-dns" version = "0.5.7" @@ -11730,10 +12066,10 @@ dependencies = [ "futures-timer", "gtk4", "hex", - "image", "mimalloc", "names", "native-dialog", + "notify-rust", "open", "pallet-balances", "parity-scale-codec", @@ -12478,6 +12814,17 @@ version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" +[[package]] +name = "tauri-winrt-notification" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f89f5fb70d6f62381f5d9b2ba9008196150b40b75f3068eb24faeddf1c686871" +dependencies = [ + "quick-xml", + "windows 0.56.0", + "windows-version", +] + [[package]] name = "tempfile" version = "3.10.1" @@ -12564,6 +12911,17 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "time" version = "0.3.36" @@ -13308,6 +13666,17 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + [[package]] name = "valuable" version = "0.1.0" @@ -13746,6 +14115,12 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + [[package]] name = "wfd" version = "0.1.7" @@ -13984,6 +14359,15 @@ dependencies = [ "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-version" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6998aa457c9ba8ff2fb9f13e9d2a930dabcea28f1d0ab94d687d8b3654844515" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -14521,6 +14905,30 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" +dependencies = [ + "zune-core", +] + [[package]] name = "zvariant" version = "3.7.1" diff --git a/Cargo.toml b/Cargo.toml index 66fd760d..57af1283 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,9 +54,9 @@ futures = "0.3.30" futures-timer = "3.0.3" gtk = { version = "0.7.3", package = "gtk4" } hex = "0.4.3" -image = { version = "0.25.1", default-features = false, features = ["png"] } mimalloc = "0.1.41" names = "0.14.0" +notify-rust = { version = "4.11.1", features = ["images"] } open = "5.2.0" pallet-balances = { git = "https://github.com/subspace/polkadot-sdk", rev = "0cbfcb0232bbf71ac5b14cc8c99bf043cec420ef", default-features = false } parity-scale-codec = "3.6.12" diff --git a/src/frontend.rs b/src/frontend.rs index 87dfdec7..3c998462 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -14,11 +14,14 @@ use crate::frontend::new_version::NewVersion; use crate::frontend::running::{RunningInit, RunningInput, RunningOutput, RunningView}; use crate::frontend::translations::{AsDefaultStr, T}; use crate::AppStatusCode; -use betrayer::{Icon, Menu, MenuItem, TrayEvent, TrayIcon, TrayIconBuilder}; +#[cfg(any(target_os = "linux", windows))] +use betrayer::Icon; +use betrayer::{Menu, MenuItem, TrayEvent, TrayIcon, TrayIconBuilder}; use futures::channel::mpsc; use futures::{SinkExt, StreamExt}; +use gtk::glib; use gtk::prelude::*; -use gtk::{gio, glib}; +use notify_rust::Notification; use relm4::actions::{RelmAction, RelmActionGroup}; use relm4::prelude::*; use relm4::{Sender, ShutdownReceiver}; @@ -31,14 +34,72 @@ use std::{env, fmt}; use tracing::{debug, error, warn}; pub const GLOBAL_CSS: &str = include_str!("../res/app.css"); +#[cfg(all(unix, not(target_os = "macos")))] const ICON: &[u8] = include_bytes!("../res/icon.png"); const ABOUT_IMAGE: &[u8] = include_bytes!("../res/about.png"); +#[cfg(all(unix, not(target_os = "macos")))] #[thread_local] static PIXBUF_ICON: LazyCell = LazyCell::new(|| { gtk::gdk_pixbuf::Pixbuf::from_read(ICON).expect("Statically correct image; qed") }); +trait NotificationExt { + fn with_typical_options(&mut self) -> &mut Self; +} + +impl NotificationExt for Notification { + fn with_typical_options(&mut self) -> &mut Self { + #[cfg(all(unix, not(target_os = "macos")))] + { + use notify_rust::Image; + + let image = if PIXBUF_ICON.has_alpha() { + Image::from_rgba( + PIXBUF_ICON.width(), + PIXBUF_ICON.height(), + PIXBUF_ICON.read_pixel_bytes().to_vec(), + ) + .expect("Image is statically correct; qed") + } else { + Image::from_rgb( + PIXBUF_ICON.width(), + PIXBUF_ICON.height(), + PIXBUF_ICON.read_pixel_bytes().to_vec(), + ) + .expect("Image is statically correct; qed") + }; + + // This is how we set an icon on Linux + self.image_data(image); + } + #[cfg(windows)] + { + // UUID comes from https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid + // and the whole things is auto-generated for application's Start icon (see + // get-StartApps in PowerShell), this is the `AppUserModelId` on Windows. + const APP_USER_MODEL_ID: &str = + "{6D809377-6AF0-444B-8957-A3773F02200E}\\Space Acres\\bin\\space-acres.exe"; + // This is how we'll get proper icon and application name on Windows (only when + // installed though) + self.app_id(APP_USER_MODEL_ID); + } + #[cfg(target_os = "macos")] + { + static INIT_APPLICATION_ONCE: std::sync::Once = std::sync::Once::new(); + + INIT_APPLICATION_ONCE.call_once(|| { + // Our bundle identifier for macOS package for notifications + if let Err(error) = notify_rust::set_application("xyz.autonomys.space-acres") { + error!(%error, "Failed to set application bundle identifier") + } + }) + } + + self + } +} + #[thread_local] static PIXBUF_ABOUT_IMG: LazyCell = LazyCell::new(|| { gtk::gdk_pixbuf::Pixbuf::from_read(ABOUT_IMAGE).expect("Statically correct image; qed") @@ -533,24 +594,19 @@ impl AsyncComponent for App { glib::Propagation::Stop }); - #[cfg(any(target_os = "macos", target_os = "linux"))] - let tray_img = { - let img = image::load_from_memory_with_format(ICON, image::ImageFormat::Png) - .expect("Tray icon is a valid PNG; qed"); - Icon::from_rgba(img.to_rgba8().into_vec(), img.width(), img.height()) - .expect("Betrayer normalization tray icon; qed") - }; - - #[cfg(target_os = "windows")] - let tray_img = Icon::from_resource(1, None).expect("Tray icon is a valid ICO; qed"); - // TODO: Re-enable macOS once https://github.com/subspace/space-acres/issues/183 and/or // https://github.com/subspace/space-acres/issues/222 are resolved let tray_icon = if cfg!(target_os = "macos") { None } else { - TrayIconBuilder::new() - .with_icon(tray_img) + let tray_icon = TrayIconBuilder::new(); + #[cfg(target_os = "linux")] + let tray_icon = tray_icon + .with_icon(Icon::from_png_bytes(ICON).expect("Statically correct image; qed")); + #[cfg(windows)] + let tray_icon = tray_icon + .with_icon(Icon::from_resource(1, None).expect("Tray icon is a valid ICO; qed")); + tray_icon .with_tooltip("Space Acres") .with_menu(Menu::new([ MenuItem::button(T.tray_icon_open(), TrayMenuSignal::Open), @@ -670,17 +726,23 @@ impl AsyncComponent for App { }); if has_tray_icon { root.connect_hide({ + let sender = sender.clone(); let notification_shown_already = Cell::new(false); move |_window| { if !notification_shown_already.replace(true) { - let notification = - gio::Notification::new(&T.notification_app_minimized_to_tray()); - notification.set_body(Some(&T.notification_app_minimized_to_tray_body())); - // TODO: This icon is not rendered properly for some reason - notification.set_icon(&*PIXBUF_ICON); - notification.set_priority(gio::NotificationPriority::Low); - relm4::main_application().send_notification(None, ¬ification); + sender.spawn_command(|_sender| { + let mut notification = Notification::new(); + notification + .summary(&T.notification_app_minimized_to_tray()) + .body(&T.notification_app_minimized_to_tray_body()) + .with_typical_options(); + #[cfg(all(unix, not(target_os = "macos")))] + notification.urgency(notify_rust::Urgency::Low); + if let Err(error) = notification.show() { + warn!(%error, "Failed to show desktop notification"); + } + }); } } }); @@ -871,7 +933,7 @@ impl App { fn process_command(&mut self, input: AppCommandOutput, sender: AsyncComponentSender) { match input { AppCommandOutput::BackendNotification(notification) => { - self.process_backend_notification(notification); + self.process_backend_notification(notification, sender); } AppCommandOutput::Restart => { sender.input(AppInput::Restart); @@ -882,7 +944,11 @@ impl App { } } - fn process_backend_notification(&mut self, notification: BackendNotification) { + fn process_backend_notification( + &mut self, + notification: BackendNotification, + sender: AsyncComponentSender, + ) { debug!(?notification, "New backend notification"); match notification { @@ -974,12 +1040,18 @@ impl App { .emit(RunningInput::FarmerNotification(farmer_notification)); } BackendNotification::Stopped { error } => { - let notification = gio::Notification::new(&T.notification_stopped_with_error()); - notification.set_body(Some(&T.notification_stopped_with_error_body())); - // TODO: This icon is not rendered properly for some reason - notification.set_icon(&*PIXBUF_ICON); - notification.set_priority(gio::NotificationPriority::High); - relm4::main_application().send_notification(None, ¬ification); + sender.spawn_command(|_sender| { + let mut notification = Notification::new(); + notification + .summary(&T.notification_stopped_with_error()) + .body(&T.notification_stopped_with_error_body()) + .with_typical_options(); + #[cfg(all(unix, not(target_os = "macos")))] + notification.urgency(notify_rust::Urgency::Critical); + if let Err(error) = notification.show() { + warn!(%error, "Failed to show desktop notification"); + } + }); self.set_current_view(View::Stopped(error)); } diff --git a/src/frontend/running.rs b/src/frontend/running.rs index e3ca3967..9a1d97cf 100644 --- a/src/frontend/running.rs +++ b/src/frontend/running.rs @@ -11,9 +11,9 @@ use crate::frontend::translations::{AsDefaultStr, T}; use crate::frontend::widgets::progress_circle::{ ProgressCircle, ProgressCircleInit, ProgressCircleInput, }; -use crate::frontend::PIXBUF_ICON; -use gtk::gio; +use crate::frontend::NotificationExt; use gtk::prelude::*; +use notify_rust::Notification; use relm4::factory::FactoryHashMap; use relm4::prelude::*; use relm4_icons::icon_name; @@ -25,7 +25,7 @@ use subspace_farmer::farm::{ FarmingNotification, ProvingResult, SectorPlottingDetails, SectorUpdate, }; use subspace_runtime_primitives::{Balance, SSC}; -use tracing::{debug, error}; +use tracing::{debug, error, warn}; #[derive(Debug)] pub struct RunningInit { @@ -453,31 +453,27 @@ impl RunningView { notification, } => { if let FarmingNotification::Proving(proving_details) = ¬ification { - let notification = match proving_details.result { + let mut notification = Notification::new(); + match proving_details.result { ProvingResult::Success => { - let notification = gio::Notification::new( - &T.notification_signed_reward_successfully(), - ); - notification.set_body(Some( - &T.notification_signed_reward_successfully_body(), - )); - notification + .summary(&T.notification_signed_reward_successfully()) + .body(&T.notification_signed_reward_successfully_body()); } ProvingResult::Timeout | ProvingResult::Rejected | ProvingResult::Failed => { - let notification = - gio::Notification::new(&T.notification_missed_reward()); - notification.set_body(Some(&T.notification_missed_reward_body())); - notification + .summary(&T.notification_missed_reward()) + .body(&T.notification_missed_reward_body()); } - }; + } - // TODO: This icon is not rendered properly for some reason - notification.set_icon(&*PIXBUF_ICON); - relm4::main_application().send_notification(None, ¬ification); + sender.spawn_command(move |_sender| { + if let Err(error) = notification.with_typical_options().show() { + warn!(%error, "Failed to show desktop notification"); + } + }); } self.farms.send( &farm_index, diff --git a/src/frontend/running/farm.rs b/src/frontend/running/farm.rs index 4d8d167c..ed8513e4 100644 --- a/src/frontend/running/farm.rs +++ b/src/frontend/running/farm.rs @@ -1,9 +1,9 @@ use crate::backend::farmer::DiskFarm; use crate::frontend::translations::{AsDefaultStr, T}; -use crate::frontend::PIXBUF_ICON; +use crate::frontend::NotificationExt; use bytesize::ByteSize; -use gtk::gio; use gtk::prelude::*; +use notify_rust::Notification; use relm4::prelude::*; use relm4_icons::icon_name; use simple_moving_average::{SingleSumSMA, SMA}; @@ -16,7 +16,7 @@ use subspace_farmer::farm::{ FarmingError, FarmingNotification, ProvingResult, SectorExpirationDetails, SectorPlottingDetails, SectorUpdate, }; -use tracing::error; +use tracing::{error, warn}; const INVALID_SCORE_VALUE: f64 = -1.0; /// Experimentally found number that is good for default window size to not have horizontal scroll @@ -439,16 +439,16 @@ impl FactoryComponent for FarmWidget { } } - fn update(&mut self, input: Self::Input, _sender: FactorySender) { + fn update(&mut self, input: Self::Input, sender: FactorySender) { // Reset changes self.reset(); - self.process_input(input); + self.process_input(input, sender); } } impl FarmWidget { - fn process_input(&mut self, input: FarmWidgetInput) { + fn process_input(&mut self, input: FarmWidgetInput, sender: FactorySender) { match input { FarmWidgetInput::SectorUpdate { sector_index, @@ -587,12 +587,18 @@ impl FarmWidget { self.set_farm_details(!self.farm_details); } FarmWidgetInput::Error { error } => { - let notification = gio::Notification::new(&T.notification_farm_error()); - notification.set_body(Some(&T.notification_farm_error_body())); - // TODO: This icon is not rendered properly for some reason - notification.set_icon(&*PIXBUF_ICON); - notification.set_priority(gio::NotificationPriority::High); - relm4::main_application().send_notification(None, ¬ification); + sender.spawn_command(|_sender| { + let mut notification = Notification::new(); + notification + .summary(&T.notification_farm_error()) + .body(&T.notification_farm_error_body()) + .with_typical_options(); + #[cfg(all(unix, not(target_os = "macos")))] + notification.urgency(notify_rust::Urgency::Critical); + if let Err(error) = notification.show() { + warn!(%error, "Failed to show desktop notification"); + } + }); self.get_mut_error().replace(error); }