239 lines
7.7 KiB
Rust
239 lines
7.7 KiB
Rust
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<Mutex<ServerSettings>>,
|
|
should_run: Arc<Mutex<bool>>,
|
|
}
|
|
|
|
#[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<ServerMessage>) {
|
|
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<Mutex<bool>>) {
|
|
while *should_run.lock().unwrap() {
|
|
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
|
}
|
|
}
|
|
|
|
fn run_ui(
|
|
settings: Arc<Mutex<ServerSettings>>,
|
|
should_run: Arc<Mutex<bool>>,
|
|
tx: mpsc::Sender<ServerMessage>,
|
|
ui_visible: Arc<AtomicBool>,
|
|
) {
|
|
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<Mutex<ServerSettings>>,
|
|
should_run: Arc<Mutex<bool>>,
|
|
tx: mpsc::Sender<ServerMessage>,
|
|
ui_visible: Arc<AtomicBool>,
|
|
}
|
|
|
|
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
|
|
}
|