app
KiCad - the KiCad Bridge
Public Made by Adomby adom
Drive your own KiCad from an AI, in the background: open every editor, read the whole board as data (the EDA Bridge Standard board model), stack-up, part heights, netlists, place, route, pour, DRC and exports.
master
John Lauer
rust: Windows window-control layer (enumerate, process, messages, menu, focus, capture, UIA), all cross-process calls h…
3ecdded
2mo ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
//! Window pixels for the canvas-painted probe: PrintWindow(PW_RENDERFULLCONTENT) into a
//! compatible bitmap, read back with GetDIBits. Only the capture is ported; the image goes
//! to ab for the evidence pipeline and the PNG encoding stays out of this crate.
//! Python: kicad_ui.py `_canvas_probe` and the local half of `_screenshot_hwnd`.
use windows::Win32::Foundation::HWND;
use windows::Win32::Graphics::Gdi::{
CreateCompatibleBitmap, CreateCompatibleDC, DeleteDC, DeleteObject, GetDC, GetDIBits, ReleaseDC, SelectObject, BITMAPINFO,
BITMAPINFOHEADER, BI_RGB, DIB_RGB_COLORS, HGDIOBJ,
};
use windows::Win32::Storage::Xps::{PrintWindow, PRINT_WINDOW_FLAGS};
use windows::Win32::UI::WindowsAndMessaging::PW_RENDERFULLCONTENT;
use super::enumerate::{minimized, rect};
use super::{hwnd_u64, require_responsive};
use crate::Capture;
/// Windows renders the whole window (including the DWM-composed GL canvas) into the
/// memory DC; `rgb` comes back top-down, 3 bytes per pixel.
pub fn capture(h: HWND) -> Result<Capture, String> {
if minimized(h) {
return Err(format!("window {} is minimized; PrintWindow renders nothing for a minimized window (call show_no_activate first)", hwnd_u64(h)));
}
// PrintWindow sends WM_PRINT to the target synchronously; a hung KiCad would hold us.
require_responsive(h, "capture_window", 500)?;
let (_, _, width, height) = rect(h);
if width <= 0 || height <= 0 {
return Err(format!("window {} has an empty rect", hwnd_u64(h)));
}
// SAFETY: every GDI object created here is released on every path below; the pixel
// buffer is sized exactly width * height * 4 and GetDIBits is told the same size
// through the header. The window is responsive, so PrintWindow returns.
unsafe {
let hdc = GetDC(Some(h));
if hdc.is_invalid() {
return Err("GetDC failed".into());
}
let mem = CreateCompatibleDC(Some(hdc));
let bmp = CreateCompatibleBitmap(hdc, width, height);
if mem.is_invalid() || bmp.is_invalid() {
if !bmp.is_invalid() {
let _ = DeleteObject(HGDIOBJ(bmp.0));
}
if !mem.is_invalid() {
let _ = DeleteDC(mem);
}
ReleaseDC(Some(h), hdc);
return Err("CreateCompatibleDC/Bitmap failed".into());
}
let old = SelectObject(mem, HGDIOBJ(bmp.0));
let printed = PrintWindow(h, mem, PRINT_WINDOW_FLAGS(PW_RENDERFULLCONTENT)).as_bool();
let mut bmi = BITMAPINFO::default();
bmi.bmiHeader = BITMAPINFOHEADER {
biSize: std::mem::size_of::<BITMAPINFOHEADER>() as u32,
biWidth: width,
biHeight: -height, // negative: top-down rows, no flip needed
biPlanes: 1,
biBitCount: 32,
biCompression: BI_RGB.0,
..Default::default()
};
let mut bgra = vec![0u8; (width as usize) * (height as usize) * 4];
let lines = GetDIBits(mem, bmp, 0, height as u32, Some(bgra.as_mut_ptr() as *mut _), &mut bmi, DIB_RGB_COLORS);
SelectObject(mem, old);
let _ = DeleteObject(HGDIOBJ(bmp.0));
let _ = DeleteDC(mem);
ReleaseDC(Some(h), hdc);
if !printed {
return Err(format!("PrintWindow returned FALSE for {}", hwnd_u64(h)));
}
if lines <= 0 {
return Err("GetDIBits returned no scan lines".into());
}
let mut rgb = Vec::with_capacity((width as usize) * (height as usize) * 3);
for px in bgra.chunks_exact(4) {
rgb.extend_from_slice(&[px[2], px[1], px[0]]);
}
Ok(Capture { width: width as u32, height: height as u32, rgb })
}
}