mod window_proc;
use crate::windows_utils::console::hide_default_console_or_attach_ctrl_handler;
use crate::windows_utils::event_loop::run_message_loop;
use crate::windows_utils::hicon::application_icon::get_application_icon;
use crate::windows_utils::tray::TRAY_ICON_ID;
use crate::windows_utils::tray::add_tray_icon;
use crate::windows_utils::window::create_window_for_tray;
use eyre::Context;
use eyre::ContextCompat;
use std::ffi::c_void;
use std::sync::Mutex;
use std::sync::OnceLock;
use tracing::info;
use windows::Win32::Foundation::HWND;
use windows::Win32::UI::Shell::NIF_ICON;
use windows::Win32::UI::Shell::NIM_MODIFY;
use windows::Win32::UI::Shell::NOTIFYICONDATAW;
use windows::Win32::UI::Shell::Shell_NotifyIconW;
use windows::Win32::UI::WindowsAndMessaging::HICON;
use windows::core::w;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TrayContext {
pub inherited_console_available: bool,
}
static TRAY_WINDOW: OnceLock<isize> = OnceLock::new();
static CURRENT_ICON: OnceLock<Mutex<Option<isize>>> = OnceLock::new();
fn current_icon_slot() -> &'static Mutex<Option<isize>> {
CURRENT_ICON.get_or_init(|| Mutex::new(None))
}
#[must_use]
pub fn current_tray_icon() -> Option<HICON> {
current_icon_slot()
.lock()
.ok()
.and_then(|guard| guard.as_ref().copied())
.map(|bits| HICON(bits as *mut c_void))
}
fn record_tray_window(hwnd: HWND) {
let _ = TRAY_WINDOW.set(hwnd.0 as isize);
}
fn record_tray_icon(icon: HICON) {
*current_icon_slot()
.lock()
.expect("tray icon mutex poisoned") = Some(icon.0 as isize);
}
pub fn set_tray_icon(icon: HICON) -> eyre::Result<()> {
record_tray_icon(icon);
let hwnd_bits = *TRAY_WINDOW
.get()
.wrap_err("Tray window handle not available")?;
let hwnd = HWND(hwnd_bits as *mut c_void);
let notify_icon_data = NOTIFYICONDATAW {
cbSize: u32::try_from(std::mem::size_of::<NOTIFYICONDATAW>())
.expect("NOTIFYICONDATAW size fits in u32"),
hWnd: hwnd,
uID: TRAY_ICON_ID,
uFlags: NIF_ICON,
hIcon: icon,
..Default::default()
};
unsafe { Shell_NotifyIconW(NIM_MODIFY, &raw const notify_icon_data) }
.ok()
.wrap_err("Failed to update tray icon")?;
Ok(())
}
pub fn run_tray(context: TrayContext) -> eyre::Result<()> {
hide_default_console_or_attach_ctrl_handler()?;
window_proc::configure(window_proc::TrayWindowConfig {
inherited_console_available: context.inherited_console_available,
})?;
let window = create_window_for_tray(Some(window_proc::window_proc))?;
record_tray_window(window);
let icon = get_application_icon()?;
record_tray_icon(icon);
add_tray_icon(window, icon, w!("teamy-mft"))?;
info!("Tray initialized");
run_message_loop(None)?;
Ok(())
}