use crate::{
event_loop::UserEvent, menu_state::MenuState, server_generator::ServerGenerator,
server_loop::run_server_loop, server_status::ServerStatus,
};
use image::ImageError;
use std::time::Duration;
use take_once::TakeOnce;
use thiserror::Error;
use tokio::runtime::Runtime;
use tray_icon::{BadIcon, Icon};
use winit::{application::ApplicationHandler, event_loop::EventLoopProxy};
pub struct TrayWrapper {
icon: Icon,
version: Option<String>,
menu_state: Option<MenuState>,
runtime: Option<Runtime>,
event_loop_proxy: EventLoopProxy<UserEvent>,
server_generator: TakeOnce<ServerGenerator>,
}
impl TrayWrapper {
pub fn new(
icon_data: &[u8],
version: Option<String>,
event_loop_proxy: EventLoopProxy<UserEvent>,
server_gen: ServerGenerator,
) -> Result<Self, TrayWrapperError> {
let image = image::load_from_memory(icon_data)?.into_rgba8();
let (width, height) = image.dimensions();
let rgba = image.into_raw();
let icon = Icon::from_rgba(rgba, width, height)?;
let server_generator = TakeOnce::new_with(server_gen);
Ok(TrayWrapper {
icon,
version,
menu_state: None,
runtime: Some(Runtime::new()?),
event_loop_proxy,
server_generator,
})
}
}
impl ApplicationHandler<UserEvent> for TrayWrapper {
fn resumed(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {}
fn window_event(
&mut self,
_event_loop: &winit::event_loop::ActiveEventLoop,
_window_id: winit::window::WindowId,
_event: winit::event::WindowEvent,
) {
}
fn new_events(
&mut self,
_event_loop: &winit::event_loop::ActiveEventLoop,
cause: winit::event::StartCause,
) {
if winit::event::StartCause::Init == cause {
let Ok(mut ms) = MenuState::new(self.icon.clone(), self.version.clone()) else {
return _event_loop.exit();
};
ms.update_tray_icon(ServerStatus::StartUp);
self.menu_state = Some(ms);
let Some(rt) = &self.runtime else {
return _event_loop.exit();
};
let sg = self
.server_generator
.take()
.expect("Unable to take generator function");
let elp = self.event_loop_proxy.clone();
rt.spawn(run_server_loop(sg, elp));
}
#[cfg(target_os = "macos")]
{
use objc2_core_foundation::CFRunLoop;
let rl = CFRunLoop::main().unwrap();
CFRunLoop::wake_up(&rl);
}
}
fn user_event(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop, event: UserEvent) {
if let UserEvent::ServerExit = event {
if let Some(rt) = self.runtime.take() {
rt.shutdown_timeout(Duration::from_secs(10));
}
_event_loop.exit();
}
if let Some(ms) = &mut self.menu_state {
if ms.quit_matches(&event)
&& let Some(rt) = self.runtime.take()
{
rt.shutdown_timeout(Duration::from_secs(10));
_event_loop.exit();
return;
}
if let UserEvent::ServerStatus(s_stat) = event {
ms.update_tray_icon(s_stat);
}
}
}
}
#[derive(Error, Debug)]
pub enum TrayWrapperError {
#[error("Unable to load the icon from buffer")]
IconLoad(#[from] ImageError),
#[error("Tray Icon Bad Icon")]
BadIcon(#[from] BadIcon),
#[error("Failure to pre-create menu")]
MenuError(#[from] tray_icon::menu::Error),
#[error(transparent)]
TokioRuntimeInit(#[from] std::io::Error),
}