Skip to content

Commit

Permalink
MVP: systray in rust
Browse files Browse the repository at this point in the history
  • Loading branch information
mmrrnn committed Jan 20, 2025
1 parent 36583d5 commit d1a2b14
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 4 deletions.
7 changes: 7 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ hex = "0.4.3"
openssl = { version = "0.10", features = ["vendored"] }
ring-compat = "0.8.0"
der = "0.7.9"
human_format = "1.1.0"

[target.'cfg(windows)'.dependencies]
winreg = "0.52.0"
Expand Down
15 changes: 15 additions & 0 deletions src-tauri/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use crate::hardware::hardware_status_monitor::{HardwareStatusMonitor, PublicDevi
use crate::internal_wallet::{InternalWallet, PaperWalletConfig};
use crate::p2pool::models::{Connections, P2poolStats};
use crate::progress_tracker::ProgressTracker;
use crate::systemtray_manager::SystemTrayData;
use crate::tor_adapter::TorConfig;
use crate::utils::shutdown_utils::stop_all_processes;
use crate::wallet_adapter::{TransactionInfo, WalletBalance};
Expand Down Expand Up @@ -447,6 +448,20 @@ pub async fn get_miner_metrics(
warn!(target: LOG_TARGET, "get_miner_metrics took too long: {:?}", timer.elapsed());
}

let new_systemtray_data: SystemTrayData = SystemTrayData {
cpu_hashrate: cpu_mining_status.hash_rate,
gpu_hashrate: gpu_mining_status.hash_rate as f64,
estimated_earning: (cpu_mining_status.estimated_earnings
+ gpu_mining_status.estimated_earnings) as f64,
};

// info!(target: LOG_TARGET, "7 elapsed {:?}", timer.elapsed());
state
.systemtray_manager
.write()
.await
.update_tray(new_systemtray_data);

let metrics_ret = MinerMetrics {
sha_network_hash_rate: sha_network_hashrate,
randomx_network_hash_rate: randomx_network_hashrate,
Expand Down
10 changes: 10 additions & 0 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use p2pool::models::Connections;
use serde_json::json;
use std::fs::{remove_dir_all, remove_file};
use std::path::Path;
use systemtray_manager::SystemTrayManager;
use tauri_plugin_cli::CliExt;
use telemetry_service::TelemetryService;
use tokio::sync::watch::{self};
Expand Down Expand Up @@ -114,6 +115,7 @@ mod process_killer;
mod process_utils;
mod process_watcher;
mod progress_tracker;
mod systemtray_manager;
mod telemetry_manager;
mod telemetry_service;
mod tests;
Expand Down Expand Up @@ -220,6 +222,12 @@ async fn setup_inner(
}
}

let _ = state
.systemtray_manager
.write()
.await
.initialize_tray(app.clone());

let cpu_miner_config = state.cpu_miner_config.read().await;
let app_config = state.config.read().await;
let use_tor = app_config.use_tor();
Expand Down Expand Up @@ -800,6 +808,7 @@ struct UniverseAppState {
updates_manager: UpdatesManager,
cached_p2pool_connections: Arc<RwLock<Option<Option<Connections>>>>,
cached_miner_metrics: Arc<RwLock<Option<MinerMetrics>>>,
systemtray_manager: Arc<RwLock<SystemTrayManager>>,
}

#[derive(Clone, serde::Serialize)]
Expand Down Expand Up @@ -910,6 +919,7 @@ fn main() {
updates_manager,
cached_p2pool_connections: Arc::new(RwLock::new(None)),
cached_miner_metrics: Arc::new(RwLock::new(None)),
systemtray_manager: Arc::new(RwLock::new(SystemTrayManager::new())),
};

let app_state2 = app_state.clone();
Expand Down
200 changes: 200 additions & 0 deletions src-tauri/src/systemtray_manager.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
use human_format::Formatter;
use log::{ error, info };

use tauri::{
menu::{ Menu, MenuItem },
tray::{ TrayIcon, TrayIconBuilder, TrayIconEvent },
AppHandle,
Wry,
};

const LOG_TARGET: &str = "tari::universe::systemtray_manager";

pub enum SystrayItemId {
CpuHashrate,
GpuHashrate,
EstimatedEarning,
}

impl SystrayItemId {
pub fn to_str(&self) -> &str {
match self {
SystrayItemId::CpuHashrate => "cpu_hashrate",
SystrayItemId::GpuHashrate => "gpu_hashrate",
SystrayItemId::EstimatedEarning => "estimated_earning",
}
}

pub fn get_title(&self, value: f64) -> String {
match self {
SystrayItemId::CpuHashrate => format!("CPU Hashrate: {:.2} H/s", value),
SystrayItemId::GpuHashrate => format!("GPU Hashrate: {:.2} H/s", value),
SystrayItemId::EstimatedEarning => format!("Estimated Earning: {:.2} tXTM/Day", value),
}
}
}

pub enum CurrentOperatingSystem {
Windows,
Linux,
MacOS,
}

#[derive(Debug, Clone, Default)]
pub struct SystemTrayData {
pub cpu_hashrate: f64,
pub gpu_hashrate: f64,
pub estimated_earning: f64,
}

pub struct SystemTrayManager {
pub tray: Option<TrayIcon>,
pub menu: Option<Menu<Wry>>,
pub last_tray_data: SystemTrayData,
}

impl SystemTrayManager {
pub fn new() -> Self {
// let tray = SystemTrayManager::initialize_tray();

Self {
tray: None,
menu: None,
last_tray_data: SystemTrayData::default(),
}
}

fn detect_current_os() -> CurrentOperatingSystem {
if cfg!(target_os = "windows") {
CurrentOperatingSystem::Windows
} else if cfg!(target_os = "linux") {
CurrentOperatingSystem::Linux
} else if cfg!(target_os = "macos") {
CurrentOperatingSystem::MacOS
} else {
panic!("Unsupported OS");
}
}

fn initialize_menu(&self, app: AppHandle) -> Result<Menu<Wry>, anyhow::Error> {
info!(target: LOG_TARGET, "Initializing system tray menu");
let cpu_hashrate = MenuItem::with_id(
&app,
SystrayItemId::CpuHashrate.to_str(),
SystrayItemId::CpuHashrate.get_title(0.0),
false,
None::<&str>
)?;
let gpu_hashrate = MenuItem::with_id(
&app,
SystrayItemId::GpuHashrate.to_str(),
SystrayItemId::GpuHashrate.get_title(0.0),
false,
None::<&str>
)?;
let estimated_earning = MenuItem::with_id(
&app,
SystrayItemId::EstimatedEarning.to_str(),
SystrayItemId::EstimatedEarning.get_title(0.0),
false,
None::<&str>
)?;

let menu = Menu::with_items(&app, &[&cpu_hashrate, &gpu_hashrate, &estimated_earning])?;
Ok(menu)
}

fn get_tooltip_text(&self) -> String {
let data = self.last_tray_data.clone();
let current_os = SystemTrayManager::detect_current_os();

match current_os {
CurrentOperatingSystem::Windows => {
format!(
"Hashrate \nCPU: {} H/s\nGPU: {} H/s\nEst. earning: {} tXTM/day",
Formatter::new().with_decimals(2).with_separator("").format(data.cpu_hashrate),
// data.cpu_usage,
Formatter::new().with_decimals(2).with_separator("").format(data.gpu_hashrate),
// data.gpu_usage,
Formatter::new()
.with_decimals(2)
.with_separator("")
.format(data.estimated_earning / 1_000_000.0)
)
}
CurrentOperatingSystem::Linux => "Not supported".to_string(),
CurrentOperatingSystem::MacOS => {
format!(
"CPU:\n Hashrate: {} H/s\nGPU:\n Hashrate: {} H/s\nEst. earning: {} tXTM/day",
Formatter::new().with_decimals(0).with_separator("").format(data.cpu_hashrate),
// data.cpu_usage,
Formatter::new().with_decimals(2).with_separator("").format(data.gpu_hashrate),
// data.gpu_usage,
Formatter::new()
.with_decimals(2)
.with_separator("")
.format(data.estimated_earning / 1_000_000.0)
)
}
}
}

pub fn initialize_tray(&mut self, app: AppHandle) -> Result<(), anyhow::Error> {
let menu = self.initialize_menu(app.clone())?;
let tooltip_text = Some(self.get_tooltip_text());
let tray = TrayIconBuilder::new()
.on_tray_icon_event(move |tray, event| {
match event {
TrayIconEvent::Enter { .. } => {
println!("systemtray enter event");
let _ = tray.set_tooltip(tooltip_text.as_ref());
}
_ => {}
}
})

.menu(&menu)
.tooltip(self.get_tooltip_text())
.build(&app)?;

self.menu.replace(menu);
self.tray.replace(tray);

Ok(())
}

pub fn update_tray(&mut self, data: SystemTrayData) {
self.last_tray_data = data.clone();
match self.menu {
Some(ref menu) => {
menu.get(SystrayItemId::CpuHashrate.to_str())
.unwrap()
.as_menuitem()
.unwrap()
.set_text(SystrayItemId::CpuHashrate.get_title(data.cpu_hashrate))
.unwrap_or_else(|e| {
error!(target: LOG_TARGET, "Failed to update menu field: {}", e);
});
menu.get(SystrayItemId::GpuHashrate.to_str())
.unwrap()
.as_menuitem()
.unwrap()
.set_text(SystrayItemId::GpuHashrate.get_title(data.gpu_hashrate))
.unwrap_or_else(|e| {
error!(target: LOG_TARGET, "Failed to update menu field: {}", e);
});
menu.get(SystrayItemId::EstimatedEarning.to_str())
.unwrap()
.as_menuitem()
.unwrap()
.set_text(SystrayItemId::EstimatedEarning.get_title(data.estimated_earning))
.unwrap_or_else(|e| {
error!(target: LOG_TARGET, "Failed to update menu field: {}", e);
});
}
None => {
error!(target: LOG_TARGET, "Menu not initialized");
}
}
}
}
4 changes: 2 additions & 2 deletions src/App/AppWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect } from 'react';
import { initSystray } from '@app/utils';
// import { initSystray } from '@app/utils';

import { useDetectMode, useDisableRefresh, useLangaugeResolver, useListenForExternalDependencies } from '@app/hooks';

Expand Down Expand Up @@ -28,7 +28,7 @@ export default function AppWrapper() {
useEffect(() => {
async function initialize() {
await fetchAppConfig();
await initSystray();
// await initSystray();
await setMiningNetwork();
}
void initialize();
Expand Down
4 changes: 2 additions & 2 deletions src/containers/main/MainView.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { useUpdateSystemTray } from '@app/hooks';
// import { useUpdateSystemTray } from '@app/hooks';

import { DashboardContainer } from '@app/theme/styles.ts';
import { Dashboard } from '@app/containers/main/Dashboard';
import SideBar from '@app/containers/main/SideBar/SideBar.tsx';
import { useAppConfigStore } from '@app/store/useAppConfigStore';

export default function MainView() {
useUpdateSystemTray();
// useUpdateSystemTray();
const visualMode = useAppConfigStore((s) => s.visual_mode);
return (
<DashboardContainer $visualModeOff={!visualMode}>
Expand Down

0 comments on commit d1a2b14

Please sign in to comment.