use pixelactions_core::virtualdesk::VirtualDesktop;
use windows::Win32::Foundation::{CloseHandle, HANDLE, POINT};
use windows::Win32::Security::{GetTokenInformation, TOKEN_ELEVATION, TOKEN_QUERY, TokenElevation};
use windows::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
use windows::Win32::UI::HiDpi::{
AreDpiAwarenessContextsEqual, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2,
GetThreadDpiAwarenessContext, SetProcessDpiAwarenessContext,
};
use windows::Win32::UI::Input::KeyboardAndMouse::{
INPUT, INPUT_0, INPUT_MOUSE, MOUSEEVENTF_ABSOLUTE, MOUSEEVENTF_MOVE, MOUSEEVENTF_VIRTUALDESK,
MOUSEINPUT, SendInput,
};
use windows::Win32::UI::WindowsAndMessaging::{
GetCursorPos, GetSystemMetrics, SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, SM_XVIRTUALSCREEN,
SM_YVIRTUALSCREEN,
};
pub fn become_dpi_aware() -> bool {
unsafe { SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) }.is_ok()
}
pub fn is_per_monitor_aware_v2() -> bool {
unsafe {
AreDpiAwarenessContextsEqual(
GetThreadDpiAwarenessContext(),
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2,
)
}
.as_bool()
}
pub fn virtual_desktop() -> VirtualDesktop {
unsafe {
VirtualDesktop {
x: GetSystemMetrics(SM_XVIRTUALSCREEN),
y: GetSystemMetrics(SM_YVIRTUALSCREEN),
width: GetSystemMetrics(SM_CXVIRTUALSCREEN),
height: GetSystemMetrics(SM_CYVIRTUALSCREEN),
}
}
}
pub fn cursor_position() -> Option<(i32, i32)> {
let mut point = POINT { x: 0, y: 0 };
unsafe { GetCursorPos(&raw mut point) }.ok()?;
Some((point.x, point.y))
}
pub fn move_absolute(dx: i32, dy: i32) -> Result<(), &'static str> {
let input = INPUT {
r#type: INPUT_MOUSE,
Anonymous: INPUT_0 {
mi: MOUSEINPUT {
dx,
dy,
mouseData: 0,
dwFlags: MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK,
time: 0,
dwExtraInfo: 0,
},
},
};
let sent = unsafe { SendInput(&[input], std::mem::size_of::<INPUT>() as i32) };
if sent == 1 {
return Ok(());
}
Err(
"Windows did not accept the event. This is what UIPI looks like: a process at \
medium integrity cannot send input to an elevated window, the UAC dialog, or the \
login screen. Run the target program unelevated, or accept that it is out of reach",
)
}
pub fn is_elevated() -> Option<bool> {
let mut token = HANDLE::default();
unsafe { OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &raw mut token) }.ok()?;
let mut elevation = TOKEN_ELEVATION::default();
let mut returned = 0u32;
let result = unsafe {
GetTokenInformation(
token,
TokenElevation,
Some((&raw mut elevation).cast()),
u32::try_from(std::mem::size_of::<TOKEN_ELEVATION>()).ok()?,
&raw mut returned,
)
};
let _ = unsafe { CloseHandle(token) };
result.ok()?;
Some(elevation.TokenIsElevated != 0)
}