#[derive(Debug, Clone)]
pub struct ConsoleWindowInfo {
pub pid: u32,
pub title: String,
pub hwnd: u64,
}
#[cfg(windows)]
mod imp {
use super::ConsoleWindowInfo;
use std::collections::HashSet;
use std::time::{Duration, Instant};
use winapi::shared::minwindef::{BOOL, LPARAM, TRUE};
use winapi::shared::windef::HWND;
use winapi::um::winuser::{
EnumWindows, GetWindowTextW, GetWindowThreadProcessId, IsWindowVisible,
};
fn enumerate_visible_windows() -> Vec<ConsoleWindowInfo> {
let mut results: Vec<ConsoleWindowInfo> = Vec::new();
unsafe extern "system" fn enum_callback(hwnd: HWND, lparam: LPARAM) -> BOOL {
if IsWindowVisible(hwnd) == 0 {
return TRUE; }
let mut pid: u32 = 0;
GetWindowThreadProcessId(hwnd, &mut pid);
let mut title_buf: [u16; 512] = [0u16; 512];
let len = GetWindowTextW(hwnd, title_buf.as_mut_ptr(), title_buf.len() as i32);
let title = if len > 0 {
String::from_utf16_lossy(&title_buf[..len as usize])
} else {
String::new()
};
let results = &mut *(lparam as *mut Vec<ConsoleWindowInfo>);
results.push(ConsoleWindowInfo {
pid,
title,
hwnd: hwnd as u64,
});
TRUE }
unsafe {
EnumWindows(
Some(enum_callback),
&mut results as *mut Vec<ConsoleWindowInfo> as LPARAM,
);
}
results
}
pub fn monitor_console_windows(duration: Duration) -> Vec<ConsoleWindowInfo> {
let baseline: HashSet<u64> = enumerate_visible_windows()
.iter()
.map(|w| w.hwnd)
.collect();
let mut seen_new: HashSet<u64> = HashSet::new();
let mut new_windows: Vec<ConsoleWindowInfo> = Vec::new();
let start = Instant::now();
let poll_interval = Duration::from_millis(50);
while start.elapsed() < duration {
std::thread::sleep(poll_interval);
for info in enumerate_visible_windows() {
if !baseline.contains(&info.hwnd) && seen_new.insert(info.hwnd) {
new_windows.push(info);
}
}
}
new_windows
}
}
#[cfg(windows)]
pub use imp::monitor_console_windows;
#[cfg(not(windows))]
pub fn monitor_console_windows(_duration: std::time::Duration) -> Vec<ConsoleWindowInfo> {
Vec::new()
}