use std::{error::Error, ptr};
use log::warn;
use windows::{
core::HSTRING,
Graphics::Capture::GraphicsCaptureItem,
Win32::{
Foundation::{BOOL, HWND, LPARAM, RECT, TRUE},
System::{
Threading::GetCurrentProcessId, WinRT::Graphics::Capture::IGraphicsCaptureItemInterop,
},
UI::WindowsAndMessaging::{
EnumChildWindows, FindWindowW, GetClientRect, GetDesktopWindow, GetForegroundWindow,
GetWindowLongPtrW, GetWindowTextLengthW, GetWindowTextW, GetWindowThreadProcessId,
IsWindowVisible, GWL_EXSTYLE, GWL_STYLE, WS_CHILD, WS_EX_TOOLWINDOW,
},
},
};
#[derive(thiserror::Error, Eq, PartialEq, Clone, Copy, Debug)]
pub enum WindowErrors {
#[error("Failed To Get The Foreground Window")]
NoActiveWindow,
#[error("Failed To Find Window")]
NotFound,
}
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
pub struct Window {
window: HWND,
}
impl Window {
pub fn foreground() -> Result<Self, Box<dyn Error + Send + Sync>> {
let window = unsafe { GetForegroundWindow() };
if window.0 == 0 {
return Err(Box::new(WindowErrors::NoActiveWindow));
}
Ok(Self { window })
}
pub fn from_name(title: &str) -> Result<Self, Box<dyn Error + Send + Sync>> {
let title = HSTRING::from(title);
let window = unsafe { FindWindowW(None, &title) };
if window.0 == 0 {
return Err(Box::new(WindowErrors::NotFound));
}
Ok(Self { window })
}
pub fn from_contains_name(title: &str) -> Result<Self, Box<dyn Error + Send + Sync>> {
let windows = Self::enumerate()?;
let mut target_window = None;
for window in windows {
if window.title()?.contains(title) {
target_window = Some(window);
break;
}
}
Ok(target_window.map_or_else(|| Err(Box::new(WindowErrors::NotFound)), Ok)?)
}
pub fn title(&self) -> Result<String, Box<dyn Error + Send + Sync>> {
let len = unsafe { GetWindowTextLengthW(self.window) };
let mut name = vec![0u16; len as usize + 1];
if len >= 1 {
let copied = unsafe { GetWindowTextW(self.window, &mut name) };
if copied == 0 {
return Ok(String::new());
}
}
let name = String::from_utf16(
&name
.as_slice()
.iter()
.take_while(|ch| **ch != 0x0000)
.copied()
.collect::<Vec<_>>(),
)?;
Ok(name)
}
#[must_use]
pub fn is_window_valid(window: HWND) -> bool {
if !unsafe { IsWindowVisible(window).as_bool() } {
return false;
}
let mut id = 0;
unsafe { GetWindowThreadProcessId(window, Some(&mut id)) };
if id == unsafe { GetCurrentProcessId() } {
return false;
}
let mut rect = RECT::default();
let result = unsafe { GetClientRect(window, &mut rect) };
if result.is_ok() {
let styles = unsafe { GetWindowLongPtrW(window, GWL_STYLE) };
let ex_styles = unsafe { GetWindowLongPtrW(window, GWL_EXSTYLE) };
if (ex_styles & WS_EX_TOOLWINDOW.0 as isize) != 0 {
return false;
}
if (styles & WS_CHILD.0 as isize) != 0 {
return false;
}
} else {
warn!("GetClientRect Failed");
return false;
}
true
}
pub fn enumerate() -> Result<Vec<Self>, Box<dyn Error + Send + Sync>> {
let mut windows: Vec<Self> = Vec::new();
unsafe {
EnumChildWindows(
GetDesktopWindow(),
Some(Self::enum_windows_callback),
LPARAM(ptr::addr_of_mut!(windows) as isize),
)
.ok()?;
};
Ok(windows)
}
pub fn activate(&self) {
loop {
if unsafe { GetForegroundWindow() } == self.window {
break;
}
}
}
#[must_use]
pub const fn from_raw_hwnd(window: HWND) -> Self {
Self { window }
}
#[must_use]
pub const fn as_raw_hwnd(&self) -> HWND {
self.window
}
unsafe extern "system" fn enum_windows_callback(window: HWND, vec: LPARAM) -> BOOL {
let windows = &mut *(vec.0 as *mut Vec<Self>);
if Self::is_window_valid(window) {
windows.push(Self { window });
}
TRUE
}
}
impl TryFrom<Window> for GraphicsCaptureItem {
type Error = Box<dyn Error + Send + Sync>;
fn try_from(value: Window) -> Result<Self, Self::Error> {
let window = value.as_raw_hwnd();
let interop = windows::core::factory::<Self, IGraphicsCaptureItemInterop>()?;
Ok(unsafe { interop.CreateForWindow(window)? })
}
}