app
KiCad - the KiCad Bridge
Public Made by Adomby adom
Reference implementation of the KiCad bridge: multi-instance Python server, forward path via kicad-cli, reverse path via in-process plugin. Most complex of the three bundled bridges.
← Commit history
rust: phase 3 window-control surface on the Platform trait (defaults say not implemented)
1 file changed
+121−1
rust/crates/kicad-platform/src/lib.rs+121−1@@ -8,7 +8,7 @@ //! //! Ownership: windows.rs (this thread), macos.rs (Kyle), linux.rs (Barrett). -use std::path::PathBuf;+use std::path::{Path, PathBuf}; use std::process::Command; use serde::Serialize;@@ -52,6 +52,67 @@ pub struct Capabilities { pub silent_install: bool, } +/// One top-level (or child) window as the OS reports it.+#[derive(Clone, Debug, Default, Serialize)]+pub struct WindowInfo {+ pub hwnd: u64,+ pub title: String,+ #[serde(rename = "className")]+ pub class_name: String,+ pub pid: u32,+ /// Executable base name, lower case (kicad.exe, pcbnew.exe, ...).+ pub exe: String,+ pub visible: bool,+ /// Owner window handle (0 for an unowned top level). Dialogs are owned.+ pub owner: u64,+ /// x, y, width, height in physical pixels.+ pub rect: (i32, i32, i32, i32),+ pub minimized: bool,+ pub maximized: bool,+ /// The window's thread did not answer a WM_NULL within the probe deadline.+ pub hung: bool,+}++#[derive(Clone, Debug, Default, Serialize)]+pub struct MenuItem {+ /// Menu path, top level first ("View", "3D Viewer").+ pub path: Vec<String>,+ pub id: u32,+ /// Accelerator text after the tab ("Alt+3", "Ctrl+Shift+E"), if any.+ pub accel: Option<String>,+}++#[derive(Clone, Debug, Default, Serialize)]+pub struct Placement {+ pub x: i32,+ pub y: i32,+ pub width: i32,+ pub height: i32,+ pub maximized: bool,+}++#[derive(Clone, Debug, Default, Serialize)]+pub struct ProcessInfo {+ pub pid: u32,+ pub exe: String,+ pub path: String,+ #[serde(rename = "parentPid")]+ pub parent_pid: u32,+ #[serde(rename = "startedAt")]+ pub started_at: Option<u64>,+}++/// A captured window bitmap, top-down RGB8, for the canvas-painted probe.+pub struct Capture {+ pub width: u32,+ pub height: u32,+ pub rgb: Vec<u8>,+}++fn nope<T>(what: &str) -> Result<T, String> {+ Err(format!("{what}: not implemented on {}", native().capabilities().os))+}+ pub trait Platform: Sync + Send { fn capabilities(&self) -> Capabilities; /// `kicad-cli.exe` on Windows, `kicad-cli` elsewhere.@@ -72,6 +133,65 @@ pub trait Platform: Sync + Send { fn documents_dir(&self) -> Option<PathBuf>; /// Is a process with this id alive? None when the OS layer cannot tell. fn process_alive(&self, pid: u32) -> Option<bool>;++ // ---- Phase 3: window control. Every default says "not implemented here" so an+ // OS file only implements what it supports; `capabilities()` tells callers which.++ /// Every visible top-level window.+ fn list_windows(&self) -> Result<Vec<WindowInfo>, String> { nope("list_windows") }+ /// One window by handle (visible or not).+ fn window_info(&self, _hwnd: u64) -> Result<Option<WindowInfo>, String> { nope("window_info") }+ /// Direct children of a window (class name and text), for dialog buttons and edit boxes.+ fn child_windows(&self, _hwnd: u64) -> Result<Vec<WindowInfo>, String> { nope("child_windows") }+ /// Running processes whose executable lives under `bin_dir` (or any KiCad exe when None).+ fn processes(&self, _exe_names: &[&str]) -> Result<Vec<ProcessInfo>, String> { nope("processes") }+ /// Spawn a GUI program without activating its window (SW_SHOWNOACTIVATE), stderr to a log.+ fn spawn_background(&self, _exe: &Path, _args: &[String], _env: &[(String, String)], _log: Option<&Path>) -> Result<u32, String> { nope("spawn_background") }+ fn kill_process(&self, _pid: u32) -> Result<(), String> { nope("kill_process") }+ /// Does the window's thread answer a WM_NULL within `timeout_ms`?+ fn responsive(&self, _hwnd: u64, _timeout_ms: u32) -> Result<bool, String> { nope("responsive") }+ /// Post WM_CLOSE (no wait).+ fn post_close(&self, _hwnd: u64) -> Result<(), String> { nope("post_close") }+ /// Walk the native menu bar.+ fn menu_tree(&self, _hwnd: u64) -> Result<Vec<MenuItem>, String> { nope("menu_tree") }+ /// Post WM_COMMAND with a menu id (background, no focus).+ fn menu_invoke(&self, _hwnd: u64, _id: u32) -> Result<(), String> { nope("menu_invoke") }+ /// Post a key (WM_KEYDOWN, optional WM_CHAR, WM_KEYUP) to one window's queue.+ fn post_key(&self, _hwnd: u64, _vk: u16, _ch: Option<char>) -> Result<(), String> { nope("post_key") }+ /// Post a mouse click at client coordinates to one window's queue.+ fn post_click(&self, _hwnd: u64, _x: i32, _y: i32, _double: bool) -> Result<(), String> { nope("post_click") }+ /// WM_SETTEXT on an edit control.+ fn set_text(&self, _hwnd: u64, _text: &str) -> Result<(), String> { nope("set_text") }+ /// Text of a control (WM_GETTEXT with a deadline; never blocks on a hung window).+ fn window_text(&self, _hwnd: u64, _timeout_ms: u32) -> Result<String, String> { nope("window_text") }+ /// Click a button child by label: BM_CLICK with a timeout first, UIA Invoke second.+ /// Returns the method used.+ fn click_button(&self, _dialog: u64, _labels: &[&str]) -> Result<String, String> { nope("click_button") }+ /// UI Automation: invoke the first descendant whose name contains `name`.+ fn uia_invoke(&self, _hwnd: u64, _name: &str) -> Result<String, String> { nope("uia_invoke") }+ /// UI Automation: ValuePattern.SetValue on the descendant whose name contains `name`.+ fn uia_set_value(&self, _hwnd: u64, _name: &str, _text: &str) -> Result<(), String> { nope("uia_set_value") }+ /// UI Automation: names of descendants matching a substring (bounded).+ fn uia_find(&self, _hwnd: u64, _name: &str, _limit: usize) -> Result<Vec<String>, String> { nope("uia_find") }+ fn foreground(&self) -> Result<u64, String> { nope("foreground") }+ /// Push a window to the bottom of the z-order without activating anything.+ fn push_to_background(&self, _hwnd: u64) -> Result<(), String> { nope("push_to_background") }+ /// Show without activating (SW_SHOWNOACTIVATE).+ fn show_no_activate(&self, _hwnd: u64) -> Result<(), String> { nope("show_no_activate") }+ /// The one sanctioned foreground: restore and activate (AttachThreadInput technique).+ fn bring_to_front(&self, _hwnd: u64) -> Result<bool, String> { nope("bring_to_front") }+ fn placement(&self, _hwnd: u64) -> Result<Placement, String> { nope("placement") }+ fn set_placement(&self, _hwnd: u64, _p: &Placement) -> Result<(), String> { nope("set_placement") }+ /// Is any part of the rect on a monitor?+ fn on_a_monitor(&self, _rect: (i32, i32, i32, i32)) -> Result<bool, String> { nope("on_a_monitor") }+ /// Capture a window's pixels for the canvas-painted probe (PrintWindow); no focus change.+ fn capture_window(&self, _hwnd: u64) -> Result<Capture, String> { nope("capture_window") }+ /// The GL canvas child rect (class wxGLCanvas) in window-client coordinates, if any.+ fn gl_canvas_rect(&self, _hwnd: u64) -> Result<Option<(i32, i32, i32, i32)>, String> { nope("gl_canvas_rect") }+ /// Seconds since the user last touched keyboard or mouse.+ fn seconds_since_input(&self) -> Result<f64, String> { nope("seconds_since_input") }+ /// Set process DPI awareness once at startup (no-op where irrelevant).+ fn init_process(&self) {} } /// Standard "this build cannot do that here" reply.