← Commit history

rust: phase 3 window-control surface on the Platform trait (defaults say not implemented)

John Lauer ·1c1d14b145 ·28d ago ·parent 23a8420
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.