use std::io;
use std::ptr::null_mut;
use windows_sys::Win32::UI::WindowsAndMessaging::{
CreateCursor, DestroyCursor, OCR_APPSTARTING, OCR_CROSS, OCR_HAND, OCR_HELP, OCR_IBEAM, OCR_NO,
OCR_NORMAL, OCR_SIZEALL, OCR_SIZENESW, OCR_SIZENS, OCR_SIZENWSE, OCR_SIZEWE, OCR_UP, OCR_WAIT,
SPI_SETCURSORS, SetSystemCursor, SystemParametersInfoW,
};
const CURSOR_SIDE: i32 = 32;
const MASK_BYTES: usize = 128;
const SYSTEM_CURSORS: &[u32] = &[
OCR_NORMAL,
OCR_IBEAM,
OCR_WAIT,
OCR_CROSS,
OCR_UP,
OCR_SIZENWSE,
OCR_SIZENESW,
OCR_SIZEWE,
OCR_SIZENS,
OCR_SIZEALL,
OCR_NO,
OCR_HAND,
OCR_APPSTARTING,
OCR_HELP,
];
#[derive(Default)]
pub(super) struct Cursor {
hidden: bool,
}
impl Cursor {
pub(super) fn new() -> Self {
let hidden = match restore() {
Ok(()) => false,
Err(error) => {
eprintln!("kind=cursor_restore_failed error={error}");
true
}
};
Self { hidden }
}
pub(super) fn hidden(&self) -> bool {
self.hidden
}
pub(super) fn set_hidden(&mut self, hidden: bool) {
if hidden == self.hidden {
return;
}
self.hidden = true;
let result = if hidden { hide() } else { restore() };
match result {
Ok(()) => self.hidden = hidden,
Err(error) => {
eprintln!("kind=cursor_visibility_failed error={error}");
if restore().is_ok() {
self.hidden = false;
}
}
}
}
}
impl Drop for Cursor {
fn drop(&mut self) {
if self.hidden {
self.set_hidden(false);
}
}
}
fn restore() -> io::Result<()> {
if unsafe { SystemParametersInfoW(SPI_SETCURSORS, 0, null_mut(), 0) } == 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
fn hide() -> io::Result<()> {
let and_mask = [u8::MAX; MASK_BYTES];
let xor_mask = [0u8; MASK_BYTES];
for &id in SYSTEM_CURSORS {
let cursor = unsafe {
CreateCursor(
null_mut(),
0,
0,
CURSOR_SIDE,
CURSOR_SIDE,
and_mask.as_ptr().cast(),
xor_mask.as_ptr().cast(),
)
};
if cursor.is_null() {
return Err(io::Error::last_os_error());
}
if unsafe { SetSystemCursor(cursor, id) } == 0 {
let error = io::Error::last_os_error();
unsafe { DestroyCursor(cursor) };
return Err(error);
}
}
Ok(())
}