app
Adom Desktop - 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.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
# Launch a specific editor from the KiCad project manager using UI Automation.
# Usage: launch_editor.ps1 -EditorName "Footprint Editor"
# Also supports: "Symbol Editor", "PCB Editor", "Schematic Editor", "Gerber Viewer", etc.
param(
[Parameter(Mandatory=$true)]
[string]$EditorName,
[Parameter(Mandatory=$false)]
[long]$KiCadHwnd = 0
)
Add-Type -AssemblyName UIAutomationClient
Add-Type -AssemblyName UIAutomationTypes
Add-Type -AssemblyName System.Windows.Forms
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class Win32Launch {
[DllImport("user32.dll")]
public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
}
"@
# If no HWND provided, find KiCad window
if ($KiCadHwnd -eq 0) {
$procs = Get-Process -Name "kicad" -ErrorAction SilentlyContinue
if ($procs) {
$KiCadHwnd = $procs[0].MainWindowHandle.ToInt64()
}
if ($KiCadHwnd -eq 0) {
Write-Output "ERROR:KiCad not found"
exit 1
}
}
$hwnd = [IntPtr]$KiCadHwnd
# BACKGROUND ONLY (John, 2026-07-20). UIA InvokePattern drives a control through
# the accessibility tree, which needs NO focus — so we never take the user's
# screen. SW_SHOWNOACTIVATE (4) just un-minimises so the window is screenshottable.
[Win32Launch]::ShowWindow($hwnd, 4) | Out-Null
Start-Sleep -Milliseconds 200
# Use UI Automation to find the launcher text
$root = [System.Windows.Automation.AutomationElement]::FromHandle($hwnd)
if (-not $root) {
Write-Output "ERROR:Cannot get UI Automation element for KiCad"
exit 1
}
# Search for a Text element matching the editor name
$condition = New-Object System.Windows.Automation.PropertyCondition(
[System.Windows.Automation.AutomationElement]::NameProperty,
$EditorName
)
$element = $root.FindFirst(
[System.Windows.Automation.TreeScope]::Descendants,
$condition
)
if (-not $element) {
Write-Output "ERROR:Could not find '$EditorName' in KiCad UI"
exit 1
}
# Try to get an InvokePattern (clickable element)
try {
$invokePattern = $element.GetCurrentPattern([System.Windows.Automation.InvokePattern]::Pattern)
$invokePattern.Invoke()
Write-Output "OK:Invoked $EditorName via InvokePattern"
exit 0
} catch {
# No InvokePattern — this is a text label, not a button.
# Fall back to clicking at the element's location.
}
# Get the element's bounding rectangle and click on it
$rect = $element.Current.BoundingRectangle
if ($rect.IsEmpty) {
Write-Output "ERROR:Element has no bounding rectangle"
exit 1
}
$clickX = [int]($rect.X + $rect.Width / 2)
$clickY = [int]($rect.Y + $rect.Height / 2)
# Use SendKeys mouse simulation: move cursor and click
[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point($clickX, $clickY)
Start-Sleep -Milliseconds 100
# Simulate click
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class MouseClick {
[DllImport("user32.dll")]
public static extern void mouse_event(uint dwFlags, int dx, int dy, uint dwData, UIntPtr dwExtraInfo);
}
"@
[MouseClick]::mouse_event(0x0002, 0, 0, 0, [UIntPtr]::Zero) # LEFTDOWN
Start-Sleep -Milliseconds 30
[MouseClick]::mouse_event(0x0004, 0, 0, 0, [UIntPtr]::Zero) # LEFTUP
Write-Output "OK:Clicked at ($clickX, $clickY) on $EditorName"