use crate::events::KeyEvent;
use std::sync::OnceLock;
use windows::Win32::Foundation::{BOOL, HWND, LPARAM, LRESULT, RECT, WPARAM};
use windows::Win32::Graphics::Gdi::{
EnumDisplayMonitors, GetMonitorInfoW, UpdateWindow, HDC, HMONITOR, MONITORINFO,
};
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
use windows::Win32::UI::WindowsAndMessaging::{
CreateWindowExW, DefWindowProcW, PostQuitMessage, RegisterClassExW, SetWindowPos, ShowWindow,
CS_HREDRAW, CS_VREDRAW, HWND_BOTTOM, SWP_NOACTIVATE, SWP_NOZORDER, SW_SHOW, WINDOWPOS,
WM_DESTROY, WM_KEYDOWN, WM_WINDOWPOSCHANGING, WNDCLASSEXW, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW,
WS_POPUP, WS_VISIBLE,
};
static KEY_SENDER: OnceLock<std::sync::mpsc::Sender<KeyEvent>> = OnceLock::new();
pub fn set_key_sender(sender: std::sync::mpsc::Sender<KeyEvent>) {
let _ = KEY_SENDER.set(sender);
}
fn hwnd_null(h: HWND) -> bool {
h.0 as isize == 0
}
pub fn create_wallpaper_hwnd(width: i32, height: i32) -> Option<HWND> {
unsafe {
let hinstance = GetModuleHandleW(None).ok()?;
let class_name = windows::core::w!("GeodesicWallpaper");
let wc = WNDCLASSEXW {
cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(wnd_proc),
hInstance: hinstance.into(),
lpszClassName: class_name,
..Default::default()
};
RegisterClassExW(&wc);
let hwnd = match CreateWindowExW(
WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW,
class_name,
windows::core::w!(""),
WS_POPUP | WS_VISIBLE,
0,
0,
width,
height,
None,
None,
hinstance,
None,
) {
Ok(h) => h,
Err(e) => {
tracing::error!("CreateWindowExW failed: {e}");
return None;
}
};
if hwnd_null(hwnd) {
return None;
}
let _ = SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, width, height, SWP_NOACTIVATE);
let _ = ShowWindow(hwnd, SW_SHOW);
let _ = UpdateWindow(hwnd);
tracing::info!("Wallpaper window created: {:?}", hwnd);
Some(hwnd)
}
}
pub fn enumerate_monitors() -> Vec<(i32, i32, u32, u32)> {
let mut results: Vec<(i32, i32, u32, u32)> = Vec::new();
unsafe extern "system" fn monitor_cb(
hmon: HMONITOR,
_hdc: HDC,
_rect: *mut RECT,
lparam: LPARAM,
) -> BOOL {
unsafe {
let vec = &mut *(lparam.0 as *mut Vec<(i32, i32, u32, u32)>);
let mut info = MONITORINFO {
cbSize: std::mem::size_of::<MONITORINFO>() as u32,
..Default::default()
};
if GetMonitorInfoW(hmon, &mut info).as_bool() {
let r = info.rcMonitor;
let w = (r.right - r.left).max(0) as u32;
let h = (r.bottom - r.top).max(0) as u32;
vec.push((r.left, r.top, w, h));
}
BOOL(1) }
}
unsafe {
let ptr = &mut results as *mut Vec<(i32, i32, u32, u32)>;
let _ = EnumDisplayMonitors(HDC::default(), None, Some(monitor_cb), LPARAM(ptr as isize));
}
results
}
unsafe extern "system" fn wnd_proc(hwnd: HWND, msg: u32, wp: WPARAM, lp: LPARAM) -> LRESULT {
unsafe {
if msg == WM_DESTROY {
PostQuitMessage(0);
return LRESULT(0);
}
if msg == WM_WINDOWPOSCHANGING {
let pos = &mut *(lp.0 as *mut WINDOWPOS);
pos.hwndInsertAfter = HWND_BOTTOM;
pos.flags |= SWP_NOZORDER;
return LRESULT(0);
}
if msg == WM_KEYDOWN {
let vk = wp.0 as u32;
let event = match vk {
0x5D | 0xDD => Some(KeyEvent::CycleSurface), 0x5B | 0xDB => Some(KeyEvent::CycleSurfaceBack), 0x52 => Some(KeyEvent::ResetGeodesics), 0x46 => Some(KeyEvent::ToggleFpsHud), 0xBB | 0x6B => Some(KeyEvent::SpeedUp), 0xBD | 0x6D => Some(KeyEvent::SpeedDown), 0x20 => Some(KeyEvent::TogglePause), 0x50 => Some(KeyEvent::Screenshot), _ => None,
};
if let Some(ev) = event {
if let Some(sender) = KEY_SENDER.get() {
let _ = sender.send(ev);
}
}
return LRESULT(0);
}
DefWindowProcW(hwnd, msg, wp, lp)
}
}