use std::cell::RefCell;
use windows::core::PCWSTR;
use windows::Win32::Foundation::{
CloseHandle, GetLastError, SetLastError, ERROR_ALREADY_EXISTS, HINSTANCE, HWND, LPARAM,
LRESULT, WIN32_ERROR, WPARAM,
};
use windows::Win32::System::DataExchange::COPYDATASTRUCT;
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
use windows::Win32::System::Threading::CreateMutexW;
use windows::Win32::UI::WindowsAndMessaging::{
BringWindowToTop, CreateWindowExW, DefWindowProcW, FindWindowW, IsIconic, RegisterClassExW,
SendMessageW, SetForegroundWindow, ShowWindow, HWND_MESSAGE, SW_RESTORE, WINDOW_EX_STYLE,
WINDOW_STYLE, WM_COPYDATA, WNDCLASSEXW,
};
use super::{class_name, decode_argv, encode_argv, mutex_name};
struct SiCtx {
on_second: Box<dyn FnMut(Vec<String>)>,
main_hwnd: isize,
}
thread_local! {
static SI_CTX: RefCell<Option<SiCtx>> = const { RefCell::new(None) };
}
fn wide(s: &str) -> Vec<u16> {
s.encode_utf16().chain(std::iter::once(0)).collect()
}
pub(crate) fn acquire(app_id: &str) -> bool {
let name = wide(&mutex_name(app_id));
unsafe {
SetLastError(WIN32_ERROR(0));
match CreateMutexW(None, false, PCWSTR(name.as_ptr())) {
Ok(handle) => {
let already = GetLastError() == ERROR_ALREADY_EXISTS;
if already {
let _ = CloseHandle(handle);
false
} else {
true
}
}
Err(_) => true, }
}
}
pub(crate) fn forward(app_id: &str, argv: &[String]) {
let cls = wide(&class_name(app_id));
unsafe {
let Ok(hwnd) = FindWindowW(PCWSTR(cls.as_ptr()), PCWSTR::null()) else {
return;
};
if hwnd.is_invalid() {
return;
}
let bytes = encode_argv(argv);
let cds = COPYDATASTRUCT {
dwData: 1,
cbData: bytes.len() as u32,
lpData: bytes.as_ptr() as *mut std::ffi::c_void,
};
let _ = SendMessageW(
hwnd,
WM_COPYDATA,
Some(WPARAM(0)),
Some(LPARAM(&cds as *const _ as isize)),
);
}
}
pub(crate) fn install_listener(
app_id: &str,
main_hwnd: isize,
on_second: Box<dyn FnMut(Vec<String>)>,
) {
SI_CTX.with(|c| {
*c.borrow_mut() = Some(SiCtx {
on_second,
main_hwnd,
})
});
let cls = wide(&class_name(app_id));
unsafe {
let hinst = HINSTANCE(
GetModuleHandleW(None)
.map(|h| h.0)
.unwrap_or(std::ptr::null_mut()),
);
let wc = WNDCLASSEXW {
cbSize: size_of::<WNDCLASSEXW>() as u32,
lpfnWndProc: Some(si_wnd_proc),
hInstance: hinst,
lpszClassName: PCWSTR(cls.as_ptr()),
..Default::default()
};
RegisterClassExW(&wc);
let _ = CreateWindowExW(
WINDOW_EX_STYLE::default(),
PCWSTR(cls.as_ptr()),
PCWSTR::null(),
WINDOW_STYLE::default(),
0,
0,
0,
0,
Some(HWND_MESSAGE), None,
Some(hinst),
None,
);
}
}
unsafe extern "system" fn si_wnd_proc(hwnd: HWND, msg: u32, wp: WPARAM, lp: LPARAM) -> LRESULT {
if msg == WM_COPYDATA {
let pcd = lp.0 as *const COPYDATASTRUCT;
if !pcd.is_null() && unsafe { (*pcd).dwData } == 1 {
let cb = unsafe { (*pcd).cbData } as usize;
let ptr = unsafe { (*pcd).lpData } as *const u8;
if !ptr.is_null() && cb > 0 {
let data = unsafe { std::slice::from_raw_parts(ptr, cb) };
let argv = decode_argv(data);
SI_CTX.with(|c| {
let maybe_ctx = c.borrow_mut().take();
if let Some(mut ctx) = maybe_ctx {
let main_hwnd = ctx.main_hwnd;
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
(ctx.on_second)(argv);
}));
let mut guard = c.borrow_mut();
if guard.is_none() {
*guard = Some(ctx);
}
drop(guard);
activate(main_hwnd);
}
});
}
}
return LRESULT(1);
}
unsafe { DefWindowProcW(hwnd, msg, wp, lp) }
}
pub(crate) fn activate(main_hwnd: isize) {
if main_hwnd == 0 {
return;
}
let hwnd = HWND(main_hwnd as *mut std::ffi::c_void);
unsafe {
if IsIconic(hwnd).as_bool() {
let _ = ShowWindow(hwnd, SW_RESTORE);
}
let _ = SetForegroundWindow(hwnd);
let _ = BringWindowToTop(hwnd);
}
}