use windows::{
Win32::{
Foundation::LPARAM,
Foundation::{HWND, RECT},
UI::WindowsAndMessaging::IsWindow,
},
core::BOOL,
};
use crate::{Error, Window};
pub use error::WindowsError;
pub use window::WindowsWindow;
pub type WindowsBounds = RECT;
pub type WindowsWindowId = HWND;
pub fn get_window(id: WindowsWindowId) -> Option<Window> {
let hwnd = id;
if hwnd.is_invalid() || !unsafe { IsWindow(Some(hwnd)) }.as_bool() {
None
} else {
Some(Window(WindowsWindow::new(hwnd)))
}
}
unsafe extern "system" fn enum_windows_callback(hwnd: HWND, lparam: LPARAM) -> BOOL {
let windows = unsafe { &mut *(lparam.0 as *mut Vec<Window>) };
windows.push(Window(WindowsWindow::new(hwnd)));
BOOL::from(true)
}
pub fn get_windows() -> Result<Vec<Window>, Error> {
let mut windows = Vec::new();
unsafe {
windows::Win32::UI::WindowsAndMessaging::EnumWindows(
Some(enum_windows_callback),
LPARAM(&mut windows as *const Vec<Window> as _),
)?
};
Ok(windows)
}
mod window {
use windows::Win32::{
Foundation::{self, HWND, RECT},
Graphics::Dwm::{DWMWA_EXTENDED_FRAME_BOUNDS, DwmGetWindowAttribute},
System::Threading,
UI::WindowsAndMessaging::{self, GetWindowRect},
};
use crate::Bounds;
use super::WindowsError;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct WindowsWindow(pub(crate) HWND);
unsafe impl Send for WindowsWindow {}
unsafe impl Sync for WindowsWindow {}
impl WindowsWindow {
pub fn new(hwnd: HWND) -> Self {
Self(hwnd)
}
pub fn hwnd(&self) -> HWND {
self.0
}
pub fn title(&self) -> Result<Option<String>, WindowsError> {
let mut buffer = [0u16; 256];
let length = unsafe { WindowsAndMessaging::GetWindowTextW(self.0, &mut buffer) };
if length == 0 {
let raw = windows::core::Error::from_thread();
return match raw.code() {
Foundation::S_OK => Ok(None),
_ => Err(raw),
};
}
Ok(Some(String::from_utf16_lossy(&buffer[..length as usize])))
}
pub fn rect(&self) -> Result<RECT, WindowsError> {
Ok(unsafe {
let mut rect = std::mem::zeroed();
GetWindowRect(self.0, &mut rect)?;
rect
})
}
pub fn bounds(&self) -> Result<Bounds, WindowsError> {
Ok(self.rect()?.into())
}
pub fn extended_frame_bounds(&self) -> Result<RECT, WindowsError> {
Ok(unsafe {
let mut rect: RECT = std::mem::zeroed();
DwmGetWindowAttribute(
self.0,
DWMWA_EXTENDED_FRAME_BOUNDS,
&mut rect as *mut RECT as _,
std::mem::size_of::<RECT>() as _,
)?;
rect
})
}
pub fn visible_bounds(&self) -> Result<Bounds, WindowsError> {
Ok(self.extended_frame_bounds()?.into())
}
pub fn owner_pid(&self) -> Result<u32, WindowsError> {
let mut pid = 0;
let thread =
unsafe { WindowsAndMessaging::GetWindowThreadProcessId(self.0, Some(&mut pid)) };
if thread == 0 {
Err(windows::core::Error::from_thread())
} else {
Ok(pid)
}
}
pub fn owner_process_handle(
&self,
) -> Result<windows::Win32::Foundation::HANDLE, WindowsError> {
let pid = self.owner_pid()?;
let process_handle = unsafe {
Threading::OpenProcess(
Threading::PROCESS_QUERY_INFORMATION | Threading::PROCESS_VM_READ,
false,
pid,
)?
};
Ok(process_handle)
}
pub fn owner_name(&self) -> Result<String, WindowsError> {
let process_handle = self.owner_process_handle()?;
let mut buffer = [0u16; 256];
let length = unsafe {
windows::Win32::System::ProcessStatus::GetModuleBaseNameW(
process_handle,
None,
&mut buffer,
)
};
if length == 0 {
return Err(windows::core::Error::from_thread());
}
Ok(String::from_utf16_lossy(&buffer[..length as usize]))
}
pub fn is_foreground(&self) -> bool {
self.0 == unsafe { WindowsAndMessaging::GetForegroundWindow() }
}
}
}
mod error {
pub type WindowsError = windows::core::Error;
impl From<WindowsError> for crate::Error {
fn from(error: WindowsError) -> Self {
if error.code() == windows::Win32::Foundation::E_ACCESSDENIED {
Self::PermissionDenied(error)
} else {
Self::PlatformSpecificError(error)
}
}
}
}