libobs_window_helper\util/
win_iterator.rs

1use anyhow::Result;
2use windows::{
3    core::PWSTR,
4    Win32::{
5        Foundation::HWND,
6        UI::WindowsAndMessaging::{
7            FindWindowExW, GetDesktopWindow, GetWindow, GW_CHILD, GW_HWNDNEXT,
8        },
9    },
10};
11
12use crate::{
13    get_thread_proc_id,
14    validators::{is_window_valid, WindowSearchMode},
15    window::get_window_class,
16    ProcessInfo,
17};
18
19pub unsafe fn is_uwp_window(hwnd: HWND) -> Result<bool> {
20    if hwnd.is_invalid() {
21        return Ok(false);
22    }
23
24    let class = get_window_class(hwnd)?;
25    Ok(class == "ApplicationFrameWindow")
26}
27
28pub unsafe fn get_uwp_actual_window(parent: HWND) -> Result<Option<HWND>> {
29    let ProcessInfo {
30        process_id: parent_id,
31        ..
32    } = get_thread_proc_id(parent)?;
33
34    let mut child = FindWindowExW(Some(parent), None, PWSTR::null(), PWSTR::null())?;
35
36    while !child.is_invalid() {
37        let ProcessInfo {
38            process_id: child_id,
39            ..
40        } = get_thread_proc_id(child)?;
41
42        if child_id != parent_id {
43            return Ok(Some(child));
44        }
45
46        child = FindWindowExW(Some(parent), Some(child), PWSTR::null(), PWSTR::null())
47        .unwrap_or(HWND::default());
48    }
49
50    return Ok(None);
51}
52
53pub unsafe fn next_window(
54    window: Option<HWND>,
55    mode: WindowSearchMode,
56    parent: &mut Option<HWND>,
57    use_find_window_ex: bool,
58) -> anyhow::Result<Option<HWND>> {
59    let mut window = window.unwrap_or(HWND::default());
60
61    let parent_valid = parent.is_some_and(|e| !e.is_invalid());
62    if parent_valid {
63        window = parent.unwrap_or(HWND::default());
64        *parent = None;
65    }
66
67    loop {
68        window = if use_find_window_ex {
69            FindWindowExW(Some(GetDesktopWindow()), Some(window), PWSTR::null(), PWSTR::null())
70        } else {
71            GetWindow(window, GW_HWNDNEXT)
72        }.unwrap_or(HWND::default());
73
74        let valid = is_window_valid(window, mode).ok().unwrap_or(false);
75        if window.is_invalid() || valid {
76            break;
77        }
78    }
79
80    let window_opt = if window.is_invalid() {
81        None
82    } else {
83        Some(window)
84    };
85
86    if is_uwp_window(window)? {
87        if format!("{:?}", window.0).ends_with("041098") {
88            println!("UWP Window: {:?}", window);
89        }
90        let actual = get_uwp_actual_window(window)?;
91        if let Some(child) = actual {
92            *parent = window_opt;
93
94            return Ok(Some(child));
95        }
96    }
97
98    return Ok(window_opt);
99}
100
101pub unsafe fn first_window(
102    mode: WindowSearchMode,
103    parent: &mut Option<HWND>,
104    use_find_window_ex: &mut bool,
105) -> anyhow::Result<HWND> {
106    let mut window = FindWindowExW(
107        Some(GetDesktopWindow()),
108        None,
109        PWSTR::null(),
110        PWSTR::null(),
111    )
112    .ok();
113
114    if window.is_none() {
115        *use_find_window_ex = false;
116        window = GetWindow(GetDesktopWindow(), GW_CHILD).ok();
117    } else {
118        *use_find_window_ex = true;
119    }
120
121    *parent = None;
122
123    let is_valid = window.is_some_and(|e| is_window_valid(e, mode).unwrap_or(false));
124
125    if !is_valid {
126        window = next_window(window, mode, parent, *use_find_window_ex)?;
127
128        if window.is_none() && *use_find_window_ex {
129            *use_find_window_ex = false;
130
131            window = GetWindow(GetDesktopWindow(), GW_CHILD).ok();
132            let valid = window.is_some_and(|e| is_window_valid(e, mode).unwrap_or(false));
133
134            if !valid {
135                window = next_window(window, mode, parent, *use_find_window_ex)?;
136            }
137        }
138    }
139
140    if window.is_none() {
141        return Err(anyhow::anyhow!("No window found"));
142    }
143
144    let window = window.unwrap();
145    if is_uwp_window(window)? {
146        let child = get_uwp_actual_window(window)?;
147        if let Some(c) = child {
148            *parent = Some(window);
149            return Ok(c);
150        }
151    }
152
153    return Ok(window);
154}