use axum::{routing::get, Router}; use eframe::egui; use raw_window_handle::{HasWindowHandle, RawWindowHandle}; use serde::{Deserialize, Serialize}; use std::{ net::SocketAddr, sync::{Arc, Mutex}, thread, }; use tokio::sync::mpsc; use tokio::task::JoinHandle; use tray_item::TrayItem; #[derive(Debug, Clone)] struct AppState { settings: Arc>, should_run: Arc>, } #[derive(Debug, Clone, Serialize, Deserialize)] struct ServerSettings { port: String, } 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; }); }); // Run the UI on the main thread run_ui(settings, should_run, tx); // 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 eprintln!("Server stopped."); break; }, Some(message) = rx.recv() => { match message { // This is the message type that actually restarts the server. 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::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 ServerMessage::Interact => { // Example interaction: print current settings let settings_guard = app_state.settings.lock().unwrap(); println!("Current settings: {:?}", *settings_guard); }, } } } } } 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 server = axum::Server::bind(&addr).serve(app.into_make_service()); // Run the server and monitor the should_run flag tokio::select! { _ = server => {}, _ = monitor_shutdown(server_app_state.should_run.clone()) => { println!("Server shutdown signal received"); } } }) } 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, ) { // Create an atomic bool to track window visibility static VISIBLE: once_cell::sync::Lazy> = once_cell::sync::Lazy::new(|| Mutex::new(true)); let native_options = eframe::NativeOptions::default(); eframe::run_native( "App", native_options, Box::new(|cc| { // Set up the tray icon event handler let window_handle = cc.window_handle().unwrap(); let window_handle = window_handle.as_raw(); let mut tray = TrayItem::new("App Name", tray_item::IconSource::Resource("icon-red")).unwrap(); if let RawWindowHandle::Win32(handle) = window_handle { // Windows Only. use windows::Win32::Foundation::HWND; use windows::Win32::UI::WindowsAndMessaging::{ ShowWindow, SW_HIDE, SW_RESTORE, // SW_SHOWDEFAULT, SW_SHOWNORMAL, }; tray.add_label("Server Control").unwrap(); tray.add_menu_item("Show/Hide", { move || { let mut visible_lock = VISIBLE.lock().unwrap(); let window_handle = HWND(handle.hwnd.into()); if *visible_lock { unsafe { _ = ShowWindow(window_handle, SW_HIDE); } *visible_lock = false; } else { unsafe { _ = ShowWindow(window_handle, SW_RESTORE); } *visible_lock = true; } } }) .unwrap(); } else { println!("Unsupported platform for tray icon."); } tray.add_menu_item("Quit", move || { std::process::exit(0); }) .unwrap(); let app = NativeApp { settings, should_run, tx, tray, }; Box::new(app) }), ) .expect("Error running UI."); } struct NativeApp { settings: Arc>, #[allow(dead_code)] should_run: Arc>, tx: mpsc::Sender, #[allow(dead_code)] tray: TrayItem, } impl eframe::App for NativeApp { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { 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"); } } }); } fn on_exit(&mut self, _gl: Option<&eframe::glow::Context>) { 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 }