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 5 file(s)
5 files changed
+68−8
BRIDGE_VERSION+1−1@@ -1 +1 @@-0.9.199+0.9.200
adom-bridge-kicad-manifest.json+5−5@@ -1,15 +1,15 @@ { "manifest_version": 1, "name": "kicad",- "version": "0.9.199",- "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.199/adom-bridge-kicad-v0.9.199.zip",- "sha256": "a9af58611683d9f4ed4af9ff92cd851f26eeeae7914a23d9c15938585ff76f78",- "size": 469492,+ "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, "verbPrefixes": [ "kicad_" ], "healthEndpoint": "/status",- "released_at": "2026-08-20T01:45:00Z",+ "released_at": "2026-08-20T09:35: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.199",+ "version": "0.9.200", "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+41@@ -265,6 +265,44 @@ def _write_bmp(path: str, width: int, height: int, pixel_data: bytes): f.write(bytes(row_24)) ++def _canvas_uniformity(pixels: bytes, width: int, height: int) -> 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+ properties panel while the canvas itself is blank - which is how an evidence+ thumbnail ends up proving nothing. Seen on a conference-room box where every+ capture came back with an empty canvas (ab's own capture agreed, so it is not+ the capture path); KiCad renders its canvas through OpenGL, which does not+ produce a picture on a host with no usable GPU or no active console session.++ Cheap and dependency-free: sample the canvas region straight out of the raw+ 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 x1 <= x0 or y1 <= y0:+ return {"checked": False}+ step_x = max(1, (x1 - x0) // 60)+ step_y = max(1, (y1 - y0) // 60)+ colors = set()+ samples = 0+ for y in range(y0, y1, step_y):+ row = (height - 1 - y) * width * 4 # GetDIBits rows are bottom-up+ for x in range(x0, x1, step_x):+ i = row + x * 4+ colors.add(pixels[i:i + 3])+ samples += 1+ if len(colors) > 12: # clearly drawn; stop early+ return {"checked": True, "distinctColors": len(colors),+ "rendered": True, "samples": samples}+ return {"checked": True, "distinctColors": len(colors),+ "rendered": len(colors) > 3, "samples": samples}+ except Exception:+ return {"checked": False}+ def _screenshot_hwnd(hwnd: int, label: str = "", max_dim: int = 1568) -> dict: """Capture a window by HWND using PrintWindow. Returns {success, savedTo, sizeKB}.""" # A park-minimized window (focus machinery) can't PrintWindow reliably —@@ -328,6 +366,8 @@ 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)+ gdi32.SelectObject(hdc_mem, old_bmp) gdi32.DeleteObject(hbmp) gdi32.DeleteDC(hdc_mem)@@ -377,6 +417,7 @@ def _screenshot_hwnd(hwnd: int, label: str = "", max_dim: int = 1568) -> dict: "savedTo": posix, "fullPath": posix, "sizeKB": round(size_kb, 1),+ "canvas": _canvas, }
handlers/show.py+20−1@@ -145,7 +145,8 @@ def _capture_evidence(win, label: str) -> dict | None: shot = _screenshot_hwnd(hwnd, label=label) if not shot or not shot.get("success"): return None- return {+ canvas = shot.get("canvas") or {}+ ev = { "window": title, "hwnd": hwnd, "screenshotPath": shot.get("fullPath") or shot.get("savedTo"),@@ -156,6 +157,24 @@ def _capture_evidence(win, label: str) -> dict | None: "caller's container, so it can be read or shown as a thumbnail " "without a separate pull_file."), }+ # A thumbnail whose CANVAS is empty proves the editor is on the part, not+ # that the part is drawn - and it looks like a success to anyone glancing+ # at it. Say so on the evidence itself rather than letting a blank picture+ # speak for the verb.+ if canvas.get("checked"):+ ev["canvasRendered"] = bool(canvas.get("rendered"))+ if not canvas.get("rendered"):+ ev["_canvasHint"] = (+ "The window is correct but its drawing canvas came back BLANK "+ f"({canvas.get('distinctColors')} distinct colour(s)). This is not a "+ "capture fault - KiCad draws the canvas through OpenGL, which "+ "produces no picture on a host with no usable GPU or no active "+ "console session (a locked or disconnected desktop does it too). "+ "Treat this thumbnail as proof of the WINDOW, not of the part. "+ "kicad_enable_software_opengl is the last-resort fix on a box "+ "that genuinely has no GPU."+ )+ return ev except Exception: return None