use axum::{routing::get, Router}; use eframe::egui; use serde::{Deserialize, Serialize}; use std::{ net::SocketAddr, sync::{ atomic::{AtomicBool, Ordering}, Arc, Mutex, }, thread, }; use tokio::task::JoinHandle; use tokio::{net::TcpListener, sync::mpsc}; use tray_item::TrayItem; #[derive(Debug, Clone)] struct AppState { settings: Arc>, should_run: Arc>, } #[derive(Debug, Clone, Serialize, Deserialize)] struct ServerSettings { port: String, // Change to String for text editing } async fn home() -> &'static str { "Hello, World!" } #[tokio::main] async fn main() { let (tx, rx) = mpsc::channel(32); let settings = Arc::new(Mutex::new(ServerSettings { port: "3311".to_string(), })); let should_run = Arc::new(Mutex::new(true)); let app_state = AppState { settings: settings.clone(), should_run: should_run.clone(), }; // Run the server in a separate thread let server_thread = thread::spawn(move || { let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async { run_server(app_state, rx).await; }); }); // Atomic bool to track UI visibility let ui_visible = Arc::new(AtomicBool::new(true)); // Set up the system tray icon let (tray_tx, tray_rx) = std::sync::mpsc::channel(); let mut tray = TrayItem::new( "App Name", tray_item::IconSource::Resource("icon-soundboard"), ) .unwrap(); tray.add_label("Server Control").unwrap(); tray.add_menu_item("Show/Hide", { let tray_tx = tray_tx.clone(); move || { tray_tx.send(()).unwrap(); } }) .unwrap(); tray.add_menu_item("Quit", move || { std::process::exit(0); }) .unwrap(); // Thread to handle tray icon interactions let ui_visible_clone = ui_visible.clone(); thread::spawn(move || { for _ in tray_rx { let visible = ui_visible_clone.load(Ordering::SeqCst); println!("visup"); ui_visible_clone.store(!visible, Ordering::SeqCst); } }); // Run the UI on the main thread run_ui(settings, should_run, tx, ui_visible); // Wait for the server thread to finish server_thread.join().unwrap(); } async fn run_server(app_state: AppState, mut rx: mpsc::Receiver) { let mut server_handle = start_server(&app_state); loop { tokio::select! { _ = &mut server_handle => { // Server has stopped break; }, Some(message) = rx.recv() => { match message { ServerMessage::UpdateSettings(new_settings) => { println!("Received new settings: {:?}", new_settings); let mut settings_guard = app_state.settings.lock().unwrap(); *settings_guard = new_settings; // Restart the server with new settings println!("Aborting current server..."); server_handle.abort(); // Cancel the previous server task println!("Starting new server..."); drop(settings_guard); // Ensure the lock is released before starting the new server server_handle = start_server(&app_state); println!("Server started."); }, ServerMessage::Interact => { // Example interaction: print current settings let settings_guard = app_state.settings.lock().unwrap(); println!("Current settings: {:?}", *settings_guard); }, ServerMessage::Shutdown => { // Handle server shutdown println!("Shutting down server..."); let mut should_run_guard = app_state.should_run.lock().unwrap(); *should_run_guard = false; server_handle.abort(); println!("Server shutdown."); break; }, // Handle other message types here } } } } } fn start_server(app_state: &AppState) -> JoinHandle<()> { // Compute the address before entering the async block let addr = { let settings = app_state.settings.lock().unwrap(); let port: u16 = settings.port.parse().unwrap_or(3311); SocketAddr::from(([0, 0, 0, 0], port)) }; let server_app_state = app_state.clone(); println!("listening on {}", addr); tokio::spawn(async move { let app = Router::new() .route("/", get(home)) .with_state(server_app_state.clone()); let listener = TcpListener::bind(&addr).await.unwrap(); let server = axum::serve(listener, app.into_make_service()); server.await.unwrap(); }) } async fn monitor_shutdown(should_run: Arc>) { while *should_run.lock().unwrap() { tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; } } fn run_ui( settings: Arc>, should_run: Arc>, tx: mpsc::Sender, ui_visible: Arc, ) { let app = NativeApp { settings, should_run, tx, ui_visible, }; let native_options = eframe::NativeOptions::default(); eframe::run_native("Yo", native_options, Box::new(|_cc| Box::new(app))); } struct NativeApp { settings: Arc>, should_run: Arc>, tx: mpsc::Sender, ui_visible: Arc, } impl eframe::App for NativeApp { fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { // Check visibility state and hide if needed if !self.ui_visible.load(Ordering::SeqCst) { eprintln!("frame switch!"); // Do not display the central panel if the UI should be hidden //self.ui_visible.store(true, Ordering::SeqCst); // Reset visibility state for next show } else { egui::CentralPanel::default().show(ctx, |ui| { let mut settings = self.settings.lock().unwrap(); ui.heading("Server Settings"); ui.horizontal(|ui| { ui.label("Port:"); if ui.text_edit_singleline(&mut settings.port).changed() {} }); if ui.button("Apply").clicked() { if self .tx .try_send(ServerMessage::UpdateSettings(settings.clone())) .is_err() { eprintln!("Failed to send settings update"); } } if ui.button("Interact").clicked() { if self.tx.try_send(ServerMessage::Interact).is_err() { eprintln!("Failed to send interaction message"); } } if ui.button("Shutdown Server").clicked() { if self.tx.try_send(ServerMessage::Shutdown).is_err() { eprintln!("Failed to send shutdown message"); } } }); } } } #[derive(Debug, Clone)] enum ServerMessage { UpdateSettings(ServerSettings), Interact, Shutdown, // Add other message types here }