libobs_window_helper/util/
validators.rs

1use std::os::raw::c_void;
2use windows::Win32::UI::WindowsAndMessaging::{
3    GetClientRect, GetWindowLongPtrW, IsIconic, IsWindowVisible, GWL_EXSTYLE, GWL_STYLE, WS_CHILD,
4    WS_EX_TOOLWINDOW,
5};
6use windows::{
7    core::Result,
8    Win32::{
9        Foundation::{HWND, RECT},
10        Graphics::Dwm::{DwmGetWindowAttribute, DWMWA_CLOAKED},
11    },
12};
13
14const INTERNAL_MICROSOFT_EXES_EXACT: &[&str] = &[
15    "startmenuexperiencehost.exe",
16    "applicationframehost.exe",
17    "peopleexperiencehost.exe",
18    "shellexperiencehost.exe",
19    "microsoft.notes.exe",
20    "systemsettings.exe",
21    "textinputhost.exe",
22    "searchapp.exe",
23    "video.ui.exe",
24    "searchui.exe",
25    "lockapp.exe",
26    "cortana.exe",
27    "gamebar.exe",
28    "tabtip.exe",
29    "time.exe",
30];
31
32const INTERNAL_MICROSOFT_EXES_PARTIAL: &[&str] = &["windowsinternal"];
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum WindowSearchMode {
36    ExcludeMinimized,
37    IncludeMinimized,
38}
39
40#[allow(clippy::upper_case_acronyms)]
41type DWORD = u32;
42
43pub(crate) fn is_window_cloaked(handle: HWND) -> bool {
44    let cloaked: DWORD = 0;
45    let res = unsafe {
46        DwmGetWindowAttribute(
47            handle,
48            DWMWA_CLOAKED,
49            cloaked as *mut c_void,
50            size_of::<DWORD>() as u32,
51        )
52    };
53
54    res.is_ok() && cloaked != 0
55}
56
57pub fn is_window_valid(handle: HWND, mode: WindowSearchMode) -> Result<bool> {
58    let is_visible = unsafe { IsWindowVisible(handle) };
59    if !is_visible.as_bool() {
60        return Ok(false);
61    }
62
63    if mode == WindowSearchMode::ExcludeMinimized {
64        let is_minimized = unsafe { IsIconic(handle).as_bool() } || is_window_cloaked(handle);
65        if is_minimized {
66            return Ok(false);
67        }
68    }
69
70    let mut rect = RECT::default();
71    let styles;
72    let ex_styles;
73
74    unsafe {
75        GetClientRect(handle, &mut rect)?;
76
77        // Use the W function because obs can only be compiled for 64-bit
78        styles = GetWindowLongPtrW(handle, GWL_STYLE) as DWORD;
79        ex_styles = GetWindowLongPtrW(handle, GWL_EXSTYLE) as DWORD;
80    }
81
82    if ex_styles & WS_EX_TOOLWINDOW.0 > 0 {
83        return Ok(false);
84    }
85    if styles & WS_CHILD.0 > 0 {
86        return Ok(false);
87    }
88
89    if mode == WindowSearchMode::ExcludeMinimized && (rect.bottom == 0 || rect.right == 0) {
90        return Ok(false);
91    }
92
93    Ok(true)
94}
95
96pub fn is_microsoft_internal_exe(exe: &str) -> bool {
97    let exact = INTERNAL_MICROSOFT_EXES_EXACT.contains(&exe);
98    let partial = INTERNAL_MICROSOFT_EXES_PARTIAL
99        .iter()
100        .any(|e| exe.contains(e));
101
102    exact || partial
103}