use std::{cell::RefCell, ops::Deref, path::PathBuf, rc::Rc}; use windows::Win32::{ Foundation::{BOOL, HMODULE, HWND, LPARAM, TRUE, WPARAM}, System::{ LibraryLoader::{GetModuleFileNameW, GetModuleHandleA}, Threading::GetCurrentThreadId, }, UI::WindowsAndMessaging::{ DispatchMessageW, EnumWindows, GetMessageW, PostQuitMessage, SendMessageW, TranslateMessage, MSG, WM_QUIT, WM_USER, }, }; use super::Platform; mod proxy; pub use proxy::*; #[derive(Clone, Copy)] pub struct AppRef<'a>(pub &'a HMODULE); impl<'a> types::AppRef for AppRef<'a> { fn terminate(&self) { unsafe { PostQuitMessage(0) }; } fn proxy(&self) -> Option { let thread_id = unsafe { GetCurrentThreadId() }; Some(AppProxy(thread_id)) } } pub struct AppHandle(pub HMODULE); impl types::AppHandle for AppHandle { fn singleton() -> Option { let instance = unsafe { GetModuleHandleA(None) }.ok()?; Some(AppHandle(instance)) } fn with(&self, mut fun: impl FnMut(::AppRef<'_>)) { fun(AppRef(&self.0)); } } impl AppHandle { pub unsafe fn run(&self) { with_app(None, |vars| { if let Some(delegate) = vars.delegate.borrow().deref() { delegate.on_launch(AppRef(&self.0)) } }); let mut message = MSG::default(); while GetMessageW(&mut message, None, 0, 0).into() { if message.message == WM_USER { unsafe extern "system" fn enum_func(hwnd: HWND, lparam: LPARAM) -> BOOL { SendMessageW(hwnd, WM_USER, WPARAM(0), lparam); TRUE } let _ = EnumWindows(Some(enum_func), message.lParam); continue; } _ = TranslateMessage(&message); DispatchMessageW(&message); if message.message == WM_QUIT { with_app(None, |vars| { if let Some(delegate) = vars.delegate.borrow().deref() { delegate.on_terminate(AppRef(&self.0)) } }); break; } } } } pub type RcDynDelegate = Rc>; pub struct AppVars { delegate: RefCell>, path: RefCell>, } thread_local! { static CACHE:RefCell = RefCell::new(AppVars { delegate: RefCell::new(None), path:Default::default(), }); } pub fn app_path() -> Option { if let Some(path) = CACHE.with_borrow(|vars| vars.path.borrow().as_ref().cloned()) { return Some(path); } else { const LEN: usize = 2048; let mut filename = [0u16; LEN]; let len = unsafe { GetModuleFileNameW(None, &mut filename) } as usize; if len >= LEN { return None; } #[cfg(target_os = "windows")] { use std::os::windows::prelude::OsStringExt; let path = std::ffi::OsString::from_wide(&filename[..len]).into_string().ok(); let path: Option = path.map(|v| v.into()); let path = path.map(|v| v.parent().map(|v| v.to_path_buf())).flatten(); CACHE.with_borrow_mut(|vars| { *vars.path.borrow_mut() = path.clone(); }); if let Some(path) = path { return Some(path); } } }; None } pub fn with_app(v: Option, fun: impl Fn(&AppVars) -> T) -> T { if let Some(new) = v { CACHE.with_borrow_mut(|old| { *old.delegate.borrow_mut() = Some(new); }) } CACHE.with_borrow(|v| fun(v)) }