Skip to content

Commit

Permalink
Add directx interop support
Browse files Browse the repository at this point in the history
- cargo update
- refactor window filtering code
  • Loading branch information
modelflat committed May 18, 2024
1 parent 33f45a1 commit eb164ce
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 244 deletions.
314 changes: 128 additions & 186 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ members = ["zbl", "zbl_py"]
resolver = "2"

[workspace.package]
version = "0.3.0"
version = "0.4.0"

[profile.release]
strip = true
9 changes: 5 additions & 4 deletions zbl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ once_cell = "1"
log = "0.4"

[dependencies.windows]
version = "0.54"
version = "0.56"
features = [
"Foundation",
"Graphics_Capture",
"Graphics_DirectX_Direct3D11",
"Win32_Foundation",
"Win32_Graphics_Direct3D",
"Win32_Graphics_Direct3D9",
"Win32_Graphics_Direct3D10",
"Win32_Graphics_Direct3D11",
"Win32_Graphics_Dwm",
"Win32_Graphics_Dxgi_Common",
Expand All @@ -34,7 +36,6 @@ features = [
clap = { version = "4", features = ["derive"] }

[dev-dependencies.opencv]
git = "https://github.com/modelflat/opencv-rust"
branch = "directx-interop"
version = "0.91"
default-features = false
features = ["highgui", "directx"]
features = ["highgui"]
15 changes: 6 additions & 9 deletions zbl/examples/directx_interop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,9 @@ fn main() {
};

let mut capture = Capture::new(target, true, false).expect("failed to initialize capture");
unsafe {
opencv::core::initialize_context_from_d3d11_device(&capture.d3d.device)
.expect("initialize d3d11")
};

opencv::core::initialize_context_from_d3d11_device(&mut capture.d3d.device)
.expect("initialize d3d11");

capture.start().expect("failed to start capture");

Expand All @@ -46,11 +45,9 @@ fn main() {
let mut gpu_mat = UMat::new_def();
loop {
let t_frame_start = Instant::now();
if let Some(frame) = capture.grab().expect("failed to get frame") {
unsafe {
opencv::core::convert_from_d3d11_texture_2d(&frame.texture, &mut gpu_mat)
.expect("convert from d3d11 texture")
}
if let Some(mut frame) = capture.grab().expect("failed to get frame") {
opencv::core::convert_from_d3d11_texture_2d(&mut frame.texture, &mut gpu_mat)
.expect("convert from d3d11 texture");
let t_frame_end = Instant::now();

highgui::imshow("Test", &gpu_mat).expect("failed to show frame");
Expand Down
10 changes: 9 additions & 1 deletion zbl/examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use opencv::{
core::{Mat, Size, CV_8UC4},
highgui,
};
use windows::Win32::Foundation::HWND;
use zbl::{Capturable, Capture, Display, Window};

#[derive(Parser, Debug)]
Expand All @@ -13,6 +14,8 @@ struct Args {
#[clap(long)]
window_name: Option<String>,
#[clap(long)]
window_handle: Option<isize>,
#[clap(long)]
display_id: Option<usize>,
}

Expand All @@ -23,6 +26,11 @@ fn main() {

let target = if let Some(window_name) = args.window_name {
let window = Window::find_first(&window_name).expect("failed to find window");
window.print_info();
Box::new(window) as Box<dyn Capturable>
} else if let Some(window_handle) = args.window_handle {
let window = Window::new(HWND(window_handle));
window.print_info();
Box::new(window) as Box<dyn Capturable>
} else if let Some(display_id) = args.display_id {
let display = Display::find_by_id(display_id).expect("failed to find display");
Expand All @@ -47,7 +55,7 @@ fn main() {
if let Some(frame) = capture.grab().expect("failed to get frame") {
let desc = frame.desc();
let mat = unsafe {
Mat::new_size_with_data(
Mat::new_size_with_data_unsafe(
Size::new(desc.Width as i32, desc.Height as i32),
CV_8UC4,
frame.mapped_ptr.pData,
Expand Down
121 changes: 82 additions & 39 deletions zbl/src/capture/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ extern "system" fn object_destroyed_cb(
};

if has_been_closed {
unsafe { UnhookWinEvent(this) };
unsafe {
let _ = UnhookWinEvent(this);
}
}
}
}
Expand Down Expand Up @@ -152,50 +154,73 @@ impl Window {
unsafe { IsWindowVisible(self.handle).as_bool() }
}

pub fn is_capturable(&self) -> bool {
unsafe {
if self.title.is_empty()
|| !self.is_visible()
|| self.handle == GetShellWindow()
|| self.handle == GetConsoleWindow()
|| GetAncestor(self.handle, GA_ROOT) != self.handle
{
return false;
}
}
pub fn is_shell_window(&self) -> bool {
self.handle == unsafe { GetShellWindow() }
}

let style = unsafe { GetWindowLongW(self.handle, GWL_STYLE) };
if style & (WS_DISABLED.0 as i32) == 1 {
return false;
}
pub fn is_console_window(&self) -> bool {
self.handle == unsafe { GetConsoleWindow() }
}

// No tooltips
let ex_style = unsafe { GetWindowLongW(self.handle, GWL_EXSTYLE) };
if ex_style & (WS_EX_TOOLWINDOW.0 as i32) == 1 {
return false;
}
pub fn get_root(&self) -> HWND {
unsafe { GetAncestor(self.handle, GA_ROOT) }
}

// Unfortunate work-around. Not sure how to avoid this.
if self.is_known_blocked_window() {
pub fn is_top_level(&self) -> bool {
self.get_root() == self.handle
}

/// https://learn.microsoft.com/en-us/windows/win32/winmsg/window-styles
pub fn get_style(&self) -> i32 {
unsafe { GetWindowLongW(self.handle, GWL_STYLE) }
}

/// https://learn.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles
pub fn get_ex_style(&self) -> i32 {
unsafe { GetWindowLongW(self.handle, GWL_EXSTYLE) }
}

pub fn is_disabled(&self) -> bool {
self.get_style() & (WS_DISABLED.0 as i32) == 1
}

pub fn is_tooltip(&self) -> bool {
self.get_ex_style() & (WS_EX_TOOLWINDOW.0 as i32) == 1
}

pub fn is_uwp_window(&self) -> bool {
self.class_name == "Windows.UI.Core.CoreWindow"
|| self.class_name == "ApplicationFrameWindow"
}

pub fn is_dwm_cloaked(&self) -> bool {
let mut cloaked: u32 = 0;
let dwm_attr_cloaked = unsafe {
DwmGetWindowAttribute(
self.handle,
DWMWA_CLOAKED,
&mut cloaked as *mut _ as *mut _,
std::mem::size_of::<u32>() as u32,
)
};
dwm_attr_cloaked.is_ok() && cloaked == DWM_CLOAKED_SHELL
}

pub fn is_capturable(&self) -> bool {
if !self.is_visible()
|| self.is_shell_window()
|| self.is_console_window()
|| !self.is_top_level()
|| self.is_disabled()
|| self.is_tooltip()
|| self.is_known_blocked_window()
{
return false;
}

// Check to see if the self is cloaked if it's a UWP
if self.class_name == "Windows.UI.Core.CoreWindow"
|| self.class_name == "ApplicationFrameWindow"
{
let mut cloaked: u32 = 0;
let dwm_attr_cloaked = unsafe {
DwmGetWindowAttribute(
self.handle,
DWMWA_CLOAKED,
&mut cloaked as *mut _ as *mut _,
std::mem::size_of::<u32>() as u32,
)
};
if dwm_attr_cloaked.is_ok() && cloaked == DWM_CLOAKED_SHELL {
return false;
}
if self.is_uwp_window() && self.is_dwm_cloaked() {
return false;
}

true
Expand All @@ -206,6 +231,24 @@ impl Window {
unsafe { GetWindowThreadProcessId(self.handle, Some(&mut process_id)) };
process_id
}

pub fn print_info(&self) {
println!("title = {}", self.title);
println!("class = {}", self.class_name);
println!("is_capturable = {}", self.is_capturable());
println!("\tis_visible = {}", self.is_visible());
println!("\tis_shell_window = {}", self.is_shell_window());
println!("\tis_console_window = {}", self.is_console_window());
println!("\tis_top_level = {}", self.is_top_level());
println!("\tis_disabled = {}", self.is_disabled());
println!("\tis_tooltip = {}", self.is_tooltip());
println!("\tis_uwp_window = {}", self.is_uwp_window());
println!("\tis_dwm_cloaked = {}", self.is_dwm_cloaked());
println!(
"\tis_known_blocked_window = {}",
self.is_known_blocked_window()
);
}
}

impl Capturable for Window {
Expand All @@ -220,7 +263,7 @@ impl Capturable for Window {
let mut top_left = POINT::default();
unsafe {
GetWindowRect(self.handle, &mut window_rect)?;
ClientToScreen(self.handle, &mut top_left);
let _ = ClientToScreen(self.handle, &mut top_left);
GetClientRect(self.handle, &mut client_rect)?;
}

Expand Down
3 changes: 0 additions & 3 deletions zbl_py/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,3 @@ thiserror = "1"
[dependencies.pyo3]
version = "0.21"
features = ["extension-module"]

[profile.release]
strip = true
2 changes: 1 addition & 1 deletion zbl_py/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
name = "zbl"
description = "real-time window capture library based on D3D11 and Windows.Graphics.Capture"
version = "0.3.0"
version = "0.4.0"
readme = "../README.md"
requires-python = ">=3.7"
license = { file = "../LICENSE.txt" }
Expand Down

0 comments on commit eb164ce

Please sign in to comment.