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
0.9.340: land the shipped runtime source for handlers/kicad_ui.py (tour fixes 0.9.318-0.9.339 never reached master, issue #79)
1 file changed
+78
handlers/kicad_ui.py+78@@ -573,6 +573,84 @@ def _canvas_probe(hwnd: int) -> dict | None: return None +def _canvas_content(hwnd: int) -> dict | None:+ """Where the DRAWN content sits on the GL canvas, in SCREEN pixels.++ Same GDI grab as _canvas_probe. Background = the most common sampled colour+ on the canvas; content = samples that differ from it by more than a small+ tolerance. Returns {canvas:(l,t,r,b), bbox:(l,t,r,b)|None, coverage:0..1,+ touches:[edges]} so a demo can aim its zoom at the part it is talking about+ and notice when a pan or a rotation pushed the content off the view+ (John, 2026-09-05: "don't be dumb about your zooming or panning")."""+ try:+ rect = _get_window_rect(hwnd)+ width, height = rect["width"], rect["height"]+ if width <= 0 or height <= 0:+ return None+ hdc_screen = user32.GetDC(hwnd)+ hdc_mem = gdi32.CreateCompatibleDC(hdc_screen)+ hbmp = gdi32.CreateCompatibleBitmap(hdc_screen, width, height)+ old_bmp = gdi32.SelectObject(hdc_mem, hbmp)+ user32.PrintWindow(hwnd, hdc_mem, PW_RENDERFULLCONTENT)+ class _BIH(ctypes.Structure):+ _fields_ = [("biSize", ctypes.wintypes.DWORD), ("biWidth", ctypes.wintypes.LONG),+ ("biHeight", ctypes.wintypes.LONG), ("biPlanes", ctypes.wintypes.WORD),+ ("biBitCount", ctypes.wintypes.WORD), ("biCompression", ctypes.wintypes.DWORD),+ ("biSizeImage", ctypes.wintypes.DWORD), ("biXPelsPerMeter", ctypes.wintypes.LONG),+ ("biYPelsPerMeter", ctypes.wintypes.LONG), ("biClrUsed", ctypes.wintypes.DWORD),+ ("biClrImportant", ctypes.wintypes.DWORD)]+ bmi = _BIH(); bmi.biSize = ctypes.sizeof(_BIH)+ bmi.biWidth, bmi.biHeight, bmi.biPlanes = width, -height, 1 # top-down rows+ bmi.biBitCount, bmi.biCompression = 32, BI_RGB+ pixel_buf = ctypes.create_string_buffer(width * height * 4)+ gdi32.GetDIBits(hdc_mem, hbmp, 0, height, pixel_buf, ctypes.byref(bmi), DIB_RGB_COLORS)+ buf = pixel_buf.raw+ gdi32.SelectObject(hdc_mem, old_bmp); gdi32.DeleteObject(hbmp); gdi32.DeleteDC(hdc_mem)+ user32.ReleaseDC(hwnd, hdc_screen)+ gl = _gl_canvas_rect(hwnd)+ if not gl:+ return None+ wl, wt = rect.get("x", rect.get("left", 0)), rect.get("y", rect.get("top", 0))+ l, t, r, b = gl # relative to the window box+ ins_x, ins_y = int((r - l) * 0.03), int((b - t) * 0.03)+ x0, x1, y0, y1 = max(0, l + ins_x), min(width, r - ins_x), max(0, t + ins_y), min(height, b - ins_y)+ if x1 <= x0 or y1 <= y0:+ return None+ n = 140+ sx, sy = max(1, (x1 - x0) // n), max(1, (y1 - y0) // n)+ samples = []+ for y in range(y0, y1, sy):+ row = y * width * 4+ for x in range(x0, x1, sx):+ o = row + x * 4+ samples.append((x, y, buf[o], buf[o + 1], buf[o + 2]))+ if not samples:+ return None+ from collections import Counter+ q = Counter(((bb >> 4, g >> 4, rr >> 4) for _, _, bb, g, rr in samples))+ bg = q.most_common(1)[0][0]+ def is_bg(bb, g, rr):+ return abs((bb >> 4) - bg[0]) <= 1 and abs((g >> 4) - bg[1]) <= 1 and abs((rr >> 4) - bg[2]) <= 1+ # grid dots are sparse single samples: ignore samples with no content neighbour+ cont = [(x, y) for x, y, bb, g, rr in samples if not is_bg(bb, g, rr)]+ cov = len(cont) / len(samples)+ if not cont:+ return {"canvas": (wl + l, wt + t, wl + r, wt + b), "bbox": None, "coverage": 0.0, "touches": []}+ xs = sorted(x for x, _ in cont); ys = sorted(y for _, y in cont)+ # trim 2% outliers (stray grid/axis pixels) before taking the box+ k = max(0, int(len(xs) * 0.02))+ bx0, bx1, by0, by1 = xs[k], xs[-1 - k], ys[k], ys[-1 - k]+ touches = []+ if bx0 <= x0 + 2 * sx: touches.append("left")+ if bx1 >= x1 - 2 * sx: touches.append("right")+ if by0 <= y0 + 2 * sy: touches.append("top")+ if by1 >= y1 - 2 * sy: touches.append("bottom")+ return {"canvas": (wl + l, wt + t, wl + r, wt + b),+ "bbox": (wl + bx0, wt + by0, wl + bx1, wt + by1), "coverage": round(cov, 3), "touches": touches}+ except Exception: # pylint: disable=broad-except+ return None++ def _screenshot_hwnd(hwnd: int, label: str = "", max_dim: int = 1568) -> dict: """Capture a window by HWND. AD core first, local PrintWindow as fallback.