diff --git a/.github/workflows/rust_build_and_test.yml b/.github/workflows/rust_build_and_test.yml index e2bb507..45aad09 100644 --- a/.github/workflows/rust_build_and_test.yml +++ b/.github/workflows/rust_build_and_test.yml @@ -26,7 +26,7 @@ jobs: - uses: actions/checkout@v2 - name: Install dependencies - run: sudo apt update && sudo apt install libwebkit2gtk-4.1-dev libsoup-3.0-dev + run: sudo apt update && sudo apt install libwebkit2gtk-4.1-dev libxdo-dev libsoup-3.0-dev if: contains(matrix.os, 'ubuntu') - name: Update Rust diff --git a/.github/workflows/rust_parallel_release.yml b/.github/workflows/rust_parallel_release.yml index 31c432f..862f233 100644 --- a/.github/workflows/rust_parallel_release.yml +++ b/.github/workflows/rust_parallel_release.yml @@ -67,7 +67,7 @@ jobs: run: | sudo apt update sudo apt upgrade - sudo apt install libwebkit2gtk-4.1-dev libsoup-3.0-dev upx-ucl + sudo apt install libwebkit2gtk-4.1-dev libxdo-dev libsoup-3.0-dev upx-ucl cargo install cross - name: Build and package deb packages @@ -99,7 +99,7 @@ jobs: - name: install dependencies if: contains(matrix.os, 'ubuntu') - run: sudo apt update && sudo apt install --no-install-recommends libwebkit2gtk-4.1-dev libsoup-3.0-dev upx-ucl + run: sudo apt update && sudo apt install --no-install-recommends libwebkit2gtk-4.1-dev libxdo-dev libsoup-3.0-dev upx-ucl - name: Build release binaries run: cargo build --release diff --git a/Cargo.lock b/Cargo.lock index 40846bc..fc8c7de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,6 +56,7 @@ dependencies = [ "ecies-ed25519-ng", "ed25519-dalek", "getopts", + "image", "lazy_static", "log", "lru", @@ -76,6 +77,7 @@ dependencies = [ "time", "tinyfiledialogs", "toml 0.9.8", + "tray-icon", "ureq", "uuid", "winapi", @@ -172,6 +174,9 @@ name = "bitflags" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +dependencies = [ + "serde_core", +] [[package]] name = "blake2" @@ -218,12 +223,24 @@ version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +[[package]] +name = "bytemuck" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" + [[package]] name = "byteorder" 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.10.1" @@ -742,6 +759,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + [[package]] name = "fiat-crypto" version = "0.2.9" @@ -1427,6 +1453,19 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "image" +version = "0.25.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", + "png 0.18.0", +] + [[package]] name = "indexmap" version = "2.12.0" @@ -1526,6 +1565,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.10.0", + "serde", + "unicode-segmentation", +] + [[package]] name = "kuchikiki" version = "0.8.8-speedreader" @@ -1544,12 +1594,46 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading", + "once_cell", +] + [[package]] name = "libc" version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "libredox" version = "0.1.10" @@ -1560,6 +1644,25 @@ dependencies = [ "libc", ] +[[package]] +name = "libxdo" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" +dependencies = [ + "libxdo-sys", +] + +[[package]] +name = "libxdo-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" +dependencies = [ + "libc", + "x11", +] + [[package]] name = "litemap" version = "0.8.0" @@ -1664,6 +1767,37 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "moxcms" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbdd3d7436f8b5e892b8b7ea114271ff0fa00bc5acae845d53b07d498616ef6" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "muda" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c1738382f66ed56b3b9c8119e794a2e23148ac8ea214eda86622d4cb9d415a" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "libxdo", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "once_cell", + "png 0.17.16", + "thiserror 2.0.17", + "windows-sys 0.60.2", +] + [[package]] name = "ndk" version = "0.9.0" @@ -1814,6 +1948,16 @@ dependencies = [ "objc2", ] +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.10.0", + "objc2-core-foundation", +] + [[package]] name = "objc2-encode" version = "4.1.0" @@ -1836,6 +1980,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.10.0", + "block2", "objc2", "objc2-core-foundation", ] @@ -2103,6 +2248,32 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[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]] +name = "png" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" +dependencies = [ + "bitflags 2.10.0", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -2224,6 +2395,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pxfm" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3cbdf373972bf78df4d3b518d07003938e2c7d1fb5891e55f9cb6df57009d84" +dependencies = [ + "num-traits", +] + [[package]] name = "quote" version = "1.0.41" @@ -3054,6 +3234,27 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" +[[package]] +name = "tray-icon" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d5572781bee8e3f994d7467084e1b1fd7a93ce66bd480f8156ba89dee55a2b" +dependencies = [ + "crossbeam-channel", + "dirs", + "libappindicator", + "muda", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "once_cell", + "png 0.17.16", + "thiserror 2.0.17", + "windows-sys 0.60.2", +] + [[package]] name = "typenum" version = "1.19.0" @@ -3569,6 +3770,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -3602,13 +3812,30 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + [[package]] name = "windows-threading" version = "0.1.0" @@ -3639,6 +3866,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -3651,6 +3884,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -3663,12 +3902,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -3681,6 +3932,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -3693,6 +3950,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -3705,6 +3968,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -3717,6 +3986,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" version = "0.5.40" diff --git a/Cargo.toml b/Cargo.toml index fd74931..2a2d9bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,9 +44,13 @@ thread-priority = "3.0.0" # Optional dependencies regulated by features wry = { version = "0.53", optional = true } tao = { version = "0.34", optional = true } +tray-icon = { version = "0.21.2", optional = true } tinyfiledialogs = { version = "3.9.1", optional = true } open = { version = "5.3.0", optional = true } +[target.'cfg(not(target_os = "windows"))'.dependencies] +image = { version = "0.25", default-features = false, features = ["png"] } + [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.9", features = ["impl-default", "wincon", "shellscalingapi"] } windows-service = "0.8.0" @@ -70,6 +74,6 @@ ProductName="ALFIS" FileDescription="Alternative Free Identity System" [features] -webgui = ["wry", "tao", "tinyfiledialogs", "open"] +webgui = ["wry", "tao", "tray-icon", "tinyfiledialogs", "open"] doh = ["ureq"] default = ["webgui", "doh"] diff --git a/README.md b/README.md index c5ee685..d47c6aa 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,8 @@ cargo build ``` ### ![Linux Logo](/img/linux.svg) On Linux -If you are building on Linux, you must ensure that you have `libwebkitgtk` library installed. -You can do it by issuing this command: `sudo apt install libwebkit2gtk-4.1-dev` (on Debian/Ubuntu and derivatives). +If you are building on Linux, you must ensure that you have `libwebkitgtk` and `libxdo` libraries installed (for UI and tray icon respectively). +You can do it by issuing this command: `sudo apt install libwebkit2gtk-4.1-dev libxdo-dev` (on Debian/Ubuntu and derivatives). #### ![Arch Linux Logo](/img/archlinux.svg) On Arch Linux diff --git a/src/main.rs b/src/main.rs index 68f53ac..7be3747 100644 --- a/src/main.rs +++ b/src/main.rs @@ -59,6 +59,7 @@ fn main() { opts.optflag("v", "version", "Print version and exit"); opts.optflag("d", "debug", "Show debug messages, more than usual"); opts.optflag("t", "trace", "Show trace messages, more than debug"); + opts.optflag("", "hide", "Hide UI, show only tray icon."); opts.optflag("b", "blocks", "List blocks from DB and exit"); opts.optflag("g", "generate", "Generate new config file. Generated config will be printed to console."); #[cfg(windows)] @@ -251,7 +252,7 @@ fn main() { }); } #[cfg(feature = "webgui")] - web_ui::run_interface(Arc::clone(&context), miner); + web_ui::run_interface(Arc::clone(&context), miner, opt_matches.opt_present("hide")); } // Without explicitly detaching the console cmd won't redraw it's prompt. diff --git a/src/web_ui.rs b/src/web_ui.rs index c4edc1a..5a3a1b2 100644 --- a/src/web_ui.rs +++ b/src/web_ui.rs @@ -4,6 +4,7 @@ extern crate serde_json; extern crate tinyfiledialogs as tfd; use std::sync::{Arc, Mutex, MutexGuard}; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; use std::time::Duration; @@ -17,6 +18,7 @@ use alfis::eventbus::{post, register}; use alfis::miner::Miner; use alfis::{keystore, Block, Bytes, Context, Keystore, Transaction}; use chrono::{Local, Utc}; +use image::GenericImageView; #[allow(unused_imports)] use log::{debug, error, info, trace, warn, LevelFilter}; use serde::{Deserialize, Serialize}; @@ -28,9 +30,11 @@ use tao::{ window::WindowBuilder, }; use tao::dpi::PhysicalPosition; +use tray_icon::menu::{Menu, MenuEvent, MenuItem}; +use tray_icon::{TrayIconBuilder, TrayIconEvent}; use wry::WebViewBuilder; -pub fn run_interface(context: Arc>, miner: Arc>) { +pub fn run_interface(context: Arc>, miner: Arc>, hide: bool) { let file_content = include_str!("webview/index.html"); let mut styles = inline_style(include_str!("webview/bulma.css")); styles.push_str(&inline_style(include_str!("webview/styles.css"))); @@ -42,7 +46,27 @@ pub fn run_interface(context: Arc>, miner: Arc>) { // Create event loop and window let event_loop = EventLoopBuilder::::with_user_event().build(); - let proxy = event_loop.create_proxy(); + + // Create tray menu + let tray_menu = Menu::new(); + let show_item = MenuItem::new("Show Window", true, None); + let quit_item = MenuItem::new("Quit", true, None); + tray_menu.append(&show_item).unwrap(); + tray_menu.append(&quit_item).unwrap(); + + #[cfg(windows)] + let icon = tray_icon::Icon::from_resource(1, None).unwrap(); + // Create tray icon + #[cfg(not(target_os = "windows"))] + let icon = load_icon_from_png(); + + let tray_icon = TrayIconBuilder::new() + .with_menu(Box::new(tray_menu)) + .with_tooltip(&title) + .with_icon(icon) + .with_menu_on_left_click(false) + .build() + .unwrap(); let window_size = tao::dpi::LogicalSize::new(1024, 720); // Get primary monitor and calculate center position @@ -65,7 +89,7 @@ pub fn run_interface(context: Arc>, miner: Arc>) { .with_inner_size(window_size) .with_min_inner_size(tao::dpi::LogicalSize::new(773, 350)) .with_resizable(true) - .with_visible(true); + .with_visible(!hide); if let Some(position) = position { builder = builder.with_position(position); @@ -89,6 +113,7 @@ pub fn run_interface(context: Arc>, miner: Arc>) { // Clone for the IPC handler let context_ipc = Arc::clone(&context); let miner_ipc = Arc::clone(&miner); + let proxy = event_loop.create_proxy(); let proxy_ipc = proxy.clone(); // Create webview @@ -173,10 +198,13 @@ pub fn run_interface(context: Arc>, miner: Arc>) { _ => threads }; let status = Arc::new(Mutex::new(UiStatus::new(threads))); + let connected_nodes = Arc::new(AtomicUsize::new(0)); + let nodes_copy = Arc::clone(&connected_nodes); register(move |_uuid, e| { let status = Arc::clone(&status); let proxy = proxy_events.clone(); + let nodes_copy = Arc::clone(&nodes_copy); thread::Builder::new().name(String::from("webui")).spawn(move || { let mut status = status.lock().unwrap(); @@ -267,6 +295,7 @@ pub fn run_interface(context: Arc>, miner: Arc>) { } } Event::NetworkStatus { blocks, domains, keys, nodes } => { + nodes_copy.store(nodes, Ordering::SeqCst); if status.mining || status.syncing || nodes < 3 { format!("setStats({}, {}, {}, {});", blocks, domains, keys, nodes) } else { @@ -289,6 +318,18 @@ pub fn run_interface(context: Arc>, miner: Arc>) { true }); + let proxy = event_loop.create_proxy(); + TrayIconEvent::set_event_handler(Some(move |event| { + let _ = proxy.send_event(UserEvent::TrayIconEvent(event)); + })); + + let proxy = event_loop.create_proxy(); + MenuEvent::set_event_handler(Some(move |event| { + let _ = proxy.send_event(UserEvent::MenuEvent(event)); + })); + + let proxy = event_loop.create_proxy(); + // Run event loop event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; @@ -298,10 +339,7 @@ pub fn run_interface(context: Arc>, miner: Arc>) { event: WindowEvent::CloseRequested, .. } => { - info!("Interface closed, exiting"); - post(Event::ActionQuit); - thread::sleep(Duration::from_millis(100)); - *control_flow = ControlFlow::Exit; + window.set_visible(false); } TaoEvent::UserEvent(user_event) => { let wv = webview_clone.lock().unwrap(); @@ -324,6 +362,32 @@ pub fn run_interface(context: Arc>, miner: Arc>) { UserEvent::ShowWarning(text) => { show_warning(&wv, &text); } + UserEvent::TrayIconEvent(event) => { + match event { + TrayIconEvent::DoubleClick { button, .. } => { + if button == tray_icon::MouseButton::Left { + window.set_visible(true); + window.set_focus(); + } + } + TrayIconEvent::Enter { .. } => { + let nodes = connected_nodes.load(Ordering::SeqCst); + let title = format!("ALFIS {}\nConnected: {nodes}", env!("CARGO_PKG_VERSION")); + let _ = tray_icon.set_tooltip(Some(title)); + } + _ => {} + } + } + UserEvent::MenuEvent(event) => { + if event.id == show_item.id() { + window.set_visible(true); + } else if event.id == quit_item.id() { + info!("Interface closed, exiting"); + post(Event::ActionQuit); + thread::sleep(Duration::from_millis(100)); + *control_flow = ControlFlow::Exit; + } + } } } _ => {} @@ -338,6 +402,25 @@ enum UserEvent { LoadDomains, SendKeysToUi, ShowWarning(String), + TrayIconEvent(TrayIconEvent), + MenuEvent(MenuEvent) +} + +/// Load icon from embedded in binary PNG file. Only needed in Linux/macOS builds. +#[cfg(not(target_os = "windows"))] +fn load_icon_from_png() -> tray_icon::Icon { + // Include PNG in binary + const ICON_BYTES: &[u8] = include_bytes!("../img/logo/alfis_icon32.png"); + + // decode image by crate `image` + let image = image::load_from_memory(ICON_BYTES) + .expect("Error loading image from png"); + let rgba = image.to_rgba8(); + let (width, height) = image.dimensions(); + + // Convert to format for tray_icon + tray_icon::Icon::from_rgba(rgba.into_vec(), width, height) + .expect("Error loading icon") } fn check_record(data: &str) -> bool {