use std::ptr::null_mut;
use std::sync::atomic::{AtomicBool, Ordering};
use windows::Win32::Foundation::{BOOL, FALSE, HINSTANCE};
use windows::Win32::System::Console::SetConsoleCtrlHandler;
use windows::Win32::UI::WindowsAndMessaging::{
CreateCursor, DestroyCursor, GetSystemMetrics, SetSystemCursor, SystemParametersInfoW,
OCR_NORMAL, SM_CXCURSOR, SM_CYCURSOR, SPI_SETCURSORS,
};
use windows::Win32::UI::WindowsAndMessaging::HCURSOR;
static CURSOR_HIDDEN: AtomicBool = AtomicBool::new(false);
fn create_blank_cursor() -> Result<HCURSOR, String> {
let width = unsafe { GetSystemMetrics(SM_CXCURSOR) };
let height = unsafe { GetSystemMetrics(SM_CYCURSOR) };
if width <= 0 || height <= 0 {
return Err("Could not query the system cursor size.".to_string());
}
let row_bytes = ((width as usize + 15) / 16) * 2;
let and_mask = vec![0xFFu8; row_bytes * height as usize];
let xor_mask = vec![0x00u8; row_bytes * height as usize];
let cursor = unsafe {
CreateCursor(
HINSTANCE::default(),
0,
0,
width,
height,
and_mask.as_ptr() as *const _,
xor_mask.as_ptr() as *const _,
)
}
.map_err(|e| format!("CreateCursor failed: {e}"))?;
Ok(cursor)
}
pub fn hide_system_cursor() -> Result<(), String> {
if CURSOR_HIDDEN.load(Ordering::Relaxed) {
return Ok(());
}
let blank = create_blank_cursor()?;
if let Err(e) = unsafe { SetSystemCursor(blank, OCR_NORMAL) } {
unsafe {
let _ = DestroyCursor(blank);
}
return Err(format!("SetSystemCursor failed: {e}"));
}
CURSOR_HIDDEN.store(true, Ordering::Relaxed);
Ok(())
}
pub fn restore_system_cursor() {
if CURSOR_HIDDEN.swap(false, Ordering::Relaxed) {
unsafe {
let _ = SystemParametersInfoW(SPI_SETCURSORS, 0, Some(null_mut()), Default::default());
}
}
}
unsafe extern "system" fn console_ctrl_handler(_ctrl_type: u32) -> BOOL {
restore_system_cursor();
FALSE
}
pub struct SystemCursorGuard;
impl SystemCursorGuard {
pub fn install() -> Self {
unsafe {
let _ = SetConsoleCtrlHandler(Some(console_ctrl_handler), true);
}
Self
}
}
impl Drop for SystemCursorGuard {
fn drop(&mut self) {
restore_system_cursor();
}
}