mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 21:45:31 +09:00
169 lines
4.6 KiB
Rust
169 lines
4.6 KiB
Rust
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
use crate::ScreenCaptureSourceDescriptor;
|
|
|
|
#[cfg(target_os = "windows")]
|
|
use std::ptr::null_mut;
|
|
|
|
#[cfg(target_os = "windows")]
|
|
use windows_sys::Win32::{
|
|
Foundation::{HWND, LPARAM, RECT},
|
|
Graphics::Gdi::{EnumDisplayMonitors, HMONITOR},
|
|
System::Threading::GetCurrentProcessId,
|
|
UI::WindowsAndMessaging::{
|
|
EnumWindows, GW_OWNER, GWL_STYLE, GetWindow, GetWindowLongPtrW, GetWindowRect,
|
|
GetWindowTextLengthW, GetWindowTextW, GetWindowThreadProcessId, IsWindowVisible, WS_CHILD,
|
|
},
|
|
};
|
|
|
|
#[cfg(target_os = "windows")]
|
|
const MONITOR_SOURCE_LIMIT: usize = 16;
|
|
|
|
pub fn list_sources() -> Vec<ScreenCaptureSourceDescriptor> {
|
|
#[cfg(target_os = "windows")]
|
|
{
|
|
let mut sources = enumerate_monitor_sources();
|
|
sources.extend(enumerate_window_sources());
|
|
sources
|
|
}
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
{
|
|
Vec::new()
|
|
}
|
|
}
|
|
|
|
#[cfg(target_os = "windows")]
|
|
fn enumerate_monitor_sources() -> Vec<ScreenCaptureSourceDescriptor> {
|
|
unsafe extern "system" fn enum_monitor(
|
|
_monitor: HMONITOR,
|
|
_hdc: windows_sys::Win32::Graphics::Gdi::HDC,
|
|
rect: *mut RECT,
|
|
param: LPARAM,
|
|
) -> i32 {
|
|
let monitors = &mut *(param as *mut Vec<RECT>);
|
|
if monitors.len() >= MONITOR_SOURCE_LIMIT {
|
|
return 0;
|
|
}
|
|
if !rect.is_null() {
|
|
monitors.push(*rect);
|
|
}
|
|
1
|
|
}
|
|
|
|
let mut monitors: Vec<RECT> = Vec::new();
|
|
unsafe {
|
|
EnumDisplayMonitors(
|
|
null_mut(),
|
|
null_mut(),
|
|
Some(enum_monitor),
|
|
&mut monitors as *mut _ as LPARAM,
|
|
);
|
|
}
|
|
assert!(
|
|
monitors.len() <= MONITOR_SOURCE_LIMIT,
|
|
"monitor source enumeration bounded"
|
|
);
|
|
|
|
monitors
|
|
.into_iter()
|
|
.enumerate()
|
|
.map(|(index, rect)| {
|
|
let width = (rect.right - rect.left).max(0) as u32;
|
|
let height = (rect.bottom - rect.top).max(0) as u32;
|
|
ScreenCaptureSourceDescriptor {
|
|
kind: "screen".to_string(),
|
|
id: format!("screen:{index}:0"),
|
|
name: format!("Display {}", index + 1),
|
|
width,
|
|
height,
|
|
target_pid: None,
|
|
}
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
#[cfg(target_os = "windows")]
|
|
fn enumerate_window_sources() -> Vec<ScreenCaptureSourceDescriptor> {
|
|
struct EnumState {
|
|
own_pid: u32,
|
|
sources: Vec<ScreenCaptureSourceDescriptor>,
|
|
}
|
|
|
|
unsafe extern "system" fn enum_window(hwnd: HWND, param: LPARAM) -> i32 {
|
|
let state = &mut *(param as *mut EnumState);
|
|
if let Some(source) = describe_window_source(hwnd, state.own_pid) {
|
|
state.sources.push(source);
|
|
}
|
|
1
|
|
}
|
|
|
|
let mut state = EnumState {
|
|
own_pid: unsafe { GetCurrentProcessId() },
|
|
sources: Vec::new(),
|
|
};
|
|
unsafe {
|
|
EnumWindows(Some(enum_window), &mut state as *mut _ as LPARAM);
|
|
}
|
|
state.sources
|
|
}
|
|
|
|
#[cfg(target_os = "windows")]
|
|
fn describe_window_source(hwnd: HWND, own_pid: u32) -> Option<ScreenCaptureSourceDescriptor> {
|
|
if hwnd.is_null() || unsafe { IsWindowVisible(hwnd) } == 0 {
|
|
return None;
|
|
}
|
|
if !unsafe { GetWindow(hwnd, GW_OWNER) }.is_null() {
|
|
return None;
|
|
}
|
|
let style = unsafe { GetWindowLongPtrW(hwnd, GWL_STYLE) } as u32;
|
|
if style & WS_CHILD != 0 {
|
|
return None;
|
|
}
|
|
|
|
let mut pid = 0u32;
|
|
unsafe {
|
|
GetWindowThreadProcessId(hwnd, &mut pid);
|
|
}
|
|
if pid == 0 || pid == own_pid {
|
|
return None;
|
|
}
|
|
|
|
let mut rect = RECT::default();
|
|
if unsafe { GetWindowRect(hwnd, &mut rect) } == 0 {
|
|
return None;
|
|
}
|
|
let width = (rect.right - rect.left).max(0) as u32;
|
|
let height = (rect.bottom - rect.top).max(0) as u32;
|
|
if width == 0 || height == 0 {
|
|
return None;
|
|
}
|
|
|
|
let title = window_title(hwnd).unwrap_or_else(|| format!("Window {pid}"));
|
|
Some(ScreenCaptureSourceDescriptor {
|
|
kind: "window".to_string(),
|
|
id: format!("window:{}:0", hwnd as usize),
|
|
name: title,
|
|
width,
|
|
height,
|
|
target_pid: Some(pid),
|
|
})
|
|
}
|
|
|
|
#[cfg(target_os = "windows")]
|
|
fn window_title(hwnd: HWND) -> Option<String> {
|
|
let len = unsafe { GetWindowTextLengthW(hwnd) };
|
|
if len <= 0 {
|
|
return None;
|
|
}
|
|
let mut buf = vec![0u16; (len + 1) as usize];
|
|
let copied = unsafe { GetWindowTextW(hwnd, buf.as_mut_ptr(), buf.len() as i32) };
|
|
if copied <= 0 {
|
|
return None;
|
|
}
|
|
let title = String::from_utf16_lossy(&buf[..copied as usize])
|
|
.trim()
|
|
.to_string();
|
|
if title.is_empty() { None } else { Some(title) }
|
|
}
|