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
v0.9.179: fixes wiki issue #33 - the focus GUARDIAN demoted a self-raising window on every 100ms tick with no debounce (10x/sec flicker + CPU burn). It now concedes after 3 rounds and holds the window below instead
5 files changed
+41−8
BRIDGE_VERSION+1−1@@ -1 +1 @@-0.9.178+0.9.179
adom-bridge-kicad-manifest.json+5−5@@ -1,15 +1,15 @@ { "manifest_version": 1, "name": "kicad",- "version": "0.9.178",- "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.178/adom-bridge-kicad-v0.9.178.zip",- "sha256": "c69c075b4cce46fd4478185e5485f21ae434a42a4519e0415d6e23506fdd3854",- "size": 452395,+ "version": "0.9.179",+ "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.179/adom-bridge-kicad-v0.9.179.zip",+ "sha256": "e71abb1dfec300ec654b5a99b0f15f21d4809c592e644faf2cafe3a62fbdfca0",+ "size": 452970, "verbPrefixes": [ "kicad_" ], "healthEndpoint": "/status",- "released_at": "2026-08-19T09:22:18Z",+ "released_at": "2026-08-19T17:45:00Z", "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png", "languages": [ "Python",
adom-bridge-kicad-v0.9.179.zipadded⋯ 1 unchanged line ⋯
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "kicad", "displayName": "KiCad EDA",- "version": "0.9.178",+ "version": "0.9.179", "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/win_focus.py+34−1@@ -359,6 +359,8 @@ def get_focus_debug() -> dict: # exactly this: "log what happens on how often you blink the window".) "parkFights": {str(k): v for k, v in list(_park_fights.items())[-20:]}, "parkGaveUp": len(_park_gaveup),+ "guardianDemotes": {str(k): v for k, v in list(_demote_counts.items())[-20:]},+ "guardianDemoteLimit": _DEMOTE_LIMIT, "parkFightLog": list(_park_fight_log)[-10:], "parkFightLimit": _PARK_FIGHT_LIMIT, }@@ -401,6 +403,8 @@ _park_fights: dict = {} # hwnd -> how many times we re-parked it _park_gaveup: set = set() # windows we STOPPED fighting (see _PARK_FIGHT_LIMIT) _park_fight_log: list = [] # rolling record: what we fought, how hard, and why _PARK_FIGHT_LIMIT = 3 # John, 2026-08-19: "you blink them 20 or 30 times"+_demote_counts: dict = {} # hwnd -> demotes by the GUARDIAN (wiki issue #33)+_DEMOTE_LIMIT = 3 # then hold it down once and stop re-demoting _park_attempts = 0 _park_last_error = "" _sweep_ticks = 0@@ -1102,6 +1106,7 @@ def _unpark(hwnd, to_bottom: bool = True) -> None: pos = _parked.pop(hwnd, None) _park_fights.pop(hwnd, None) _park_gaveup.discard(hwnd)+ _demote_counts.pop(hwnd, None) if pos is None or not _user32.IsWindow(hwnd): return x, y, was_max = (pos if len(pos) == 3 else (pos[0], pos[1], False))@@ -1425,7 +1430,35 @@ def restore_foreground_persistent(hwnd, seconds: float = 6.0, interval: float = _sweep_suppress_all() # catch windows CREATED mid-verb (~every 0.5s) fg = _user32.GetForegroundWindow() if fg and is_kicad_window(fg):- _push_kicad_to_background(fg)+ # WIKI ISSUE #33 (Colby Knox, independently of John's+ # report of the same class of bug): a just-launched wx+ # frame Raise()es itself repeatedly while it loads, and+ # this loop demoted it EVERY tick with no debounce - so+ # KiCad raised, we demoted, KiCad raised... at 10x/sec.+ # That is the visible "epileptic" flicker and the CPU+ # burn. One demote that STICKS is enough: after a few+ # rounds we hold the window down (bottom of z-order,+ # activation suppressed) and stop re-demoting it, which+ # also keeps the loop off the UI frame rate.+ n = _demote_counts.get(fg, 0) + 1+ _demote_counts[fg] = n+ if n <= _DEMOTE_LIMIT:+ _push_kicad_to_background(fg)+ if n == _DEMOTE_LIMIT:+ suppress_activation(fg)+ HWND_BOTTOM = 1+ _user32.SetWindowPos(fg, HWND_BOTTOM, 0, 0, 0, 0,+ 0x0010 | 0x0001 | 0x0002)+ _park_fight_log.append({+ "t": __import__("time").time(), "hwnd": int(fg),+ "fights": n,+ "action": "guardian conceded - holding it below "+ "instead of re-demoting (issue #33)",+ })+ del _park_fight_log[:-40]+ print(f"[guard] {fg}: stopped re-demoting after {n} "+ f"rounds; it self-raises while loading, so it "+ f"is held below instead", file=sys.stderr, flush=True) # NON-KiCad foreground = the user's business. We never track, # never adopt, never raise it. (v0.9.103's conditional restore # was still user-window surgery — removed entirely.)