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
Update 4 file(s)
4 files changed
+63−14
BRIDGE_VERSION+1−1@@ -1 +1 @@-0.9.200+0.9.201
adom-bridge-kicad-manifest.json+5−5@@ -1,15 +1,15 @@ { "manifest_version": 1, "name": "kicad",- "version": "0.9.200",- "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.200/adom-bridge-kicad-v0.9.200.zip",- "sha256": "799b09738cff9de8bdb1112cfe80267052403cdeaa12e7874e4e4c875cd27674",- "size": 470688,+ "version": "0.9.201",+ "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.201/adom-bridge-kicad-v0.9.201.zip",+ "sha256": "ccd4b1259f274c7c23d843d3477f905317a264ee90173cf6c692a64cefd3873f",+ "size": 471341, "verbPrefixes": [ "kicad_" ], "healthEndpoint": "/status",- "released_at": "2026-08-20T09:35:00Z",+ "released_at": "2026-08-20T09:55:00Z", "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png", "languages": [ "Python",
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "kicad", "displayName": "KiCad EDA",- "version": "0.9.200",+ "version": "0.9.201", "description": "Reverse bridge for KiCad \u2014 board/schematic introspection, lint via kicad-cli, plugin install, multi-instance probe, in-process DRC.", "homepage": "https://wiki.adom.inc/adom/adom-bridge", "author": "Adom Inc.",
handlers/kicad_ui.py+56−7@@ -266,7 +266,44 @@ def _write_bmp(path: str, width: int, height: int, pixel_data: bytes): -def _canvas_uniformity(pixels: bytes, width: int, height: int) -> dict:++def _gl_canvas_rect(hwnd: int):+ """The drawing canvas's rect, in coordinates relative to `hwnd`'s window box.++ KiCad's canvas is a `wxGLCanvas` child (confirmed live: 890x572 inside a+ Symbol Editor). Finding it beats guessing by percentage - 0.9.200 sampled a+ fixed fraction of the window and landed on the Footprint Editor's toolbar+ and Appearance panel, whose colour swatches read as "drawn" no matter what+ the canvas was doing. Returns None when no GL canvas exists."""+ try:+ found = []+ WNDENUMPROC = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.wintypes.HWND,+ ctypes.wintypes.LPARAM)++ def cb(child, _l):+ buf = ctypes.create_unicode_buffer(256)+ user32.GetClassNameW(child, buf, 256)+ if buf.value == "wxGLCanvas":+ r = ctypes.wintypes.RECT()+ user32.GetWindowRect(child, ctypes.byref(r))+ found.append((r.left, r.top, r.right, r.bottom))+ return False+ return True++ c = WNDENUMPROC(cb)+ _callbacks.append(c)+ user32.EnumChildWindows(hwnd, c, 0)+ _callbacks.remove(c)+ if not found:+ return None+ pr = ctypes.wintypes.RECT()+ user32.GetWindowRect(hwnd, ctypes.byref(pr))+ l, t, rr, b = found[0]+ return (l - pr.left, t - pr.top, rr - pr.left, b - pr.top)+ except Exception:+ return None++def _canvas_uniformity(pixels: bytes, width: int, height: int, rect=None) -> dict: """Is the editor's drawing CANVAS actually rendered, or an empty rectangle? 0.9.200. A capture can show the right title, the right tree row and the right@@ -280,9 +317,19 @@ def _canvas_uniformity(pixels: bytes, width: int, height: int) -> dict: BGRA buffer and count distinct colours. A drawn canvas carries grid dots, axes and the part itself; an unrendered one is one flat colour.""" try:- # The canvas is the large area right of the library/properties panels.- x0, x1 = int(width * 0.35), int(width * 0.95)- y0, y1 = int(height * 0.15), int(height * 0.85)+ if rect:+ # Inset a few percent so window/canvas border pixels never count as+ # "content" on an otherwise empty canvas.+ l, t, r, b = rect+ dx, dy = int((r - l) * 0.04), int((b - t) * 0.04)+ x0, x1, y0, y1 = l + dx, r - dx, t + dy, b - dy+ else:+ # No GL canvas found: fall back to a coarse guess, and say so, rather+ # than reporting a confident verdict about a region we did not locate.+ x0, x1 = int(width * 0.35), int(width * 0.95)+ y0, y1 = int(height * 0.15), int(height * 0.85)+ x0, y0 = max(0, x0), max(0, y0)+ x1, y1 = min(width, x1), min(height, y1) if x1 <= x0 or y1 <= y0: return {"checked": False} step_x = max(1, (x1 - x0) // 60)@@ -297,9 +344,11 @@ def _canvas_uniformity(pixels: bytes, width: int, height: int) -> dict: samples += 1 if len(colors) > 12: # clearly drawn; stop early return {"checked": True, "distinctColors": len(colors),- "rendered": True, "samples": samples}+ "rendered": True, "samples": samples,+ "region": "glCanvas" if rect else "approx"} return {"checked": True, "distinctColors": len(colors),- "rendered": len(colors) > 3, "samples": samples}+ "rendered": len(colors) > 3, "samples": samples,+ "region": "glCanvas" if rect else "approx"} except Exception: return {"checked": False} @@ -366,7 +415,7 @@ def _screenshot_hwnd(hwnd: int, label: str = "", max_dim: int = 1568) -> dict: pixel_buf = ctypes.create_string_buffer(buf_size) gdi32.GetDIBits(hdc_mem, hbmp, 0, height, pixel_buf, ctypes.byref(bmi), DIB_RGB_COLORS) - _canvas = _canvas_uniformity(pixel_buf.raw, width, height)+ _canvas = _canvas_uniformity(pixel_buf.raw, width, height, _gl_canvas_rect(hwnd)) gdi32.SelectObject(hdc_mem, old_bmp) gdi32.DeleteObject(hbmp)