mod menu; use tray_icon::{ menu::{AboutMetadata, Menu, MenuEvent, MenuItem, PredefinedMenuItem}, TrayIconBuilder, TrayIconEvent, }; use winit::event_loop::{ControlFlow, EventLoopBuilder}; use image; #[cfg(target_os="windows")] use winit::platform::windows::EventLoopBuilderExtWindows; use jarvis_core::config; const TRAY_ICON_BYTES: &[u8] = include_bytes!("../../../resources/icons/32x32.png"); pub fn init_blocking() { // load tray icon //let icon_path = format!("{}/../../resources/icons/{}", env!("CARGO_MANIFEST_DIR"), config::TRAY_ICON); //let icon = load_icon(std::path::Path::new(&icon_path)); let icon = load_icon_from_bytes(TRAY_ICON_BYTES); // form tray menu let tray_menu = Menu::with_items(&[ &MenuItem::new("Перезапуск", true, None), &MenuItem::new("Настройки", true, None), &MenuItem::new("Выход", true, None), ]) .unwrap(); let tray_menu = Menu::with_items(&[ &MenuItem::with_id("restart", "Перезапуск", true, None), &MenuItem::with_id("settings", "Настройки", true, None), &MenuItem::with_id("exit", "Выход", true, None), ]).unwrap(); let _tray_icon = TrayIconBuilder::new() .with_menu(Box::new(tray_menu)) .with_tooltip(config::TRAY_TOOLTIP) .with_icon(icon) .build() .unwrap(); let menu_channel = MenuEvent::receiver(); // let tray_channel = TrayIconEvent::receiver(); // @TODO: Test on Linux // We need gtk for the tray icon to show up, we need to initialize gtk and create the tray_icon #[cfg(target_os = "linux")] { gtk::init().unwrap(); glib::timeout_add_local(std::time::Duration::from_millis(100), move || { if let Ok(event) = menu_channel.try_recv() { handle_menu_event(&event); } glib::ControlFlow::Continue }); gtk::main(); } // @TODO: Test on MacOS #[cfg(target_os = "macos")] { // macOS needs proper run loop - tao or winit on main thread use winit::event_loop::{EventLoop, ControlFlow}; let event_loop = EventLoop::new().unwrap(); event_loop.run(move |_event, elwt| { elwt.set_control_flow(ControlFlow::Wait); if let Ok(event) = menu_channel.try_recv() { handle_menu_event(&event); } }).unwrap(); } #[cfg(target_os = "windows")] { // simple polling works on Windows loop { if let Ok(event) = menu_channel.try_recv() { handle_menu_event(&event); } // pump Windows messages unsafe { let mut msg: winapi::um::winuser::MSG = std::mem::zeroed(); while winapi::um::winuser::PeekMessageW( &mut msg, std::ptr::null_mut(), 0, 0, winapi::um::winuser::PM_REMOVE ) != 0 { winapi::um::winuser::TranslateMessage(&msg); winapi::um::winuser::DispatchMessageW(&msg); } } std::thread::sleep(std::time::Duration::from_millis(50)); } } info!("Tray initialized."); } fn handle_menu_event(event: &MenuEvent) { match event.id.0.as_str() { "exit" => std::process::exit(0), "restart" => { /* restart logic */ } "settings" => { /* open settings */ } _ => {} } } fn load_icon_from_bytes(bytes: &[u8]) -> tray_icon::Icon { let image = image::load_from_memory(bytes) .expect("Failed to load icon") .into_rgba8(); let (width, height) = image.dimensions(); let rgba = image.into_raw(); tray_icon::Icon::from_rgba(rgba, width, height).expect("Failed to create icon") } fn load_icon(path: &std::path::Path) -> tray_icon::Icon { let (icon_rgba, icon_width, icon_height) = { let image = image::open(path) .expect("Failed to open icon path") .into_rgba8(); let (width, height) = image.dimensions(); let rgba = image.into_raw(); (rgba, width, height) }; tray_icon::Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon") }