use std::sync::atomic::{AtomicU64, Ordering};
use parking_lot::Mutex;
use windows::Win32::{
Foundation::HWND,
UI::WindowsAndMessaging::{
GetWindowDisplayAffinity, GetWindowThreadProcessId, IsWindow, SetWindowDisplayAffinity,
WDA_EXCLUDEFROMCAPTURE, WINDOW_DISPLAY_AFFINITY,
},
};
use crate::error::Result;
#[derive(Debug, Clone, Copy)]
pub(crate) struct HideToken {
hwnd: isize,
generation: u64,
}
struct Hidden {
hwnd: isize,
generation: u64,
captures: u32,
previous: WINDOW_DISPLAY_AFFINITY,
thread: u32,
}
static HIDDEN: Mutex<Vec<Hidden>> = Mutex::new(Vec::new());
static GENERATION: AtomicU64 = AtomicU64::new(0);
pub(crate) fn hide_window_from_capture(hwnd: HWND) -> Result<HideToken> {
let key = hwnd.0 as isize;
let mut hidden = HIDDEN.lock();
let mut affinity = 0u32;
unsafe { GetWindowDisplayAffinity(hwnd, &mut affinity)? };
let thread = unsafe { GetWindowThreadProcessId(hwnd, None) };
if let Some(index) = hidden.iter().position(|entry| entry.hwnd == key) {
let entry = &mut hidden[index];
if entry.thread == thread && affinity == WDA_EXCLUDEFROMCAPTURE.0 {
entry.captures += 1;
return Ok(HideToken {
hwnd: key,
generation: entry.generation,
});
}
hidden.swap_remove(index);
}
unsafe { SetWindowDisplayAffinity(hwnd, WDA_EXCLUDEFROMCAPTURE)? };
let generation = GENERATION.fetch_add(1, Ordering::Relaxed);
hidden.push(Hidden {
hwnd: key,
generation,
captures: 1,
previous: WINDOW_DISPLAY_AFFINITY(affinity),
thread,
});
Ok(HideToken {
hwnd: key,
generation,
})
}
pub(crate) fn unhide_window(token: HideToken) {
let mut hidden = HIDDEN.lock();
let Some(index) = hidden
.iter()
.position(|entry| entry.hwnd == token.hwnd && entry.generation == token.generation)
else {
return;
};
hidden[index].captures -= 1;
if hidden[index].captures > 0 {
return;
}
let entry = hidden.swap_remove(index);
let hwnd = HWND(entry.hwnd as _);
unsafe {
let mut affinity = 0u32;
if IsWindow(Some(hwnd)).as_bool()
&& GetWindowDisplayAffinity(hwnd, &mut affinity).is_ok()
&& affinity == WDA_EXCLUDEFROMCAPTURE.0
{
let _ = SetWindowDisplayAffinity(hwnd, entry.previous);
}
}
}