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.180: NEVER blip. Remove the minimize-bounce and off-screen parking entirely - both are visible frames by construction, so capping their count could never reach zero. Backgrounding is now style + z-order only, and activation is released the moment the bridge goes idle
5 files changed
+57−136
BRIDGE_VERSION+1−1@@ -1 +1 @@-0.9.179+0.9.180
adom-bridge-kicad-manifest.json+5−5@@ -1,15 +1,15 @@ { "manifest_version": 1, "name": "kicad",- "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,+ "version": "0.9.180",+ "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.180/adom-bridge-kicad-v0.9.180.zip",+ "sha256": "6f1da6a951115abab10602076dfc15aadb31933e0ee3b5e10cebcbbe8c618176",+ "size": 452129, "verbPrefixes": [ "kicad_" ], "healthEndpoint": "/status",- "released_at": "2026-08-19T17:45:00Z",+ "released_at": "2026-08-19T18:03:58Z", "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png", "languages": [ "Python",
adom-bridge-kicad-v0.9.180.zipadded⋯ 1 unchanged line ⋯
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "kicad", "displayName": "KiCad EDA",- "version": "0.9.179",+ "version": "0.9.180", "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+50−129@@ -270,22 +270,29 @@ def _sweep_suppress_all() -> None: def _push_kicad_to_background(h) -> None:- """v0.9.104 (John, verbatim design): "you just always have to only deal with- the windows you opened from kicad" — every action here targets the KICAD- window ONLY. If it holds the foreground, MINIMIZE-BOUNCE it: minimizing the- foreground window makes WINDOWS ITSELF activate the user's previous window- (we never locate, choose, or raise any user window), then show it again- unfocused, and drop it to the bottom of the z-order."""- import time as _t+ """Put a KiCad window BEHIND, without ever making it blink.++ 0.9.180 (John, 2026-08-19, after seeing it again: "you make the window blip+ at me like it foregrounds and then it backgrounds and then it fg's and then+ it bg's - don't EVER blip it"). He is right and the old design could not+ satisfy him: it MINIMIZE-BOUNCED (SW_MINIMIZE, then SW_SHOWNOACTIVATE, then+ z-bottom). A minimize is one visible frame and the restore is another, so+ every single call was a blip BY CONSTRUCTION. Capping how many times we did+ it (0.9.175, 0.9.179) only reduced the number of blips; it could never reach+ zero.++ The only way to background a window with no visible transition is to change+ NOTHING the compositor animates: no minimize, no restore, no move, no size.+ Just z-order, once, with SWP_NOACTIVATE - plus WS_EX_NOACTIVATE so Windows+ refuses the window's own activation attempts in the first place. If the+ window still holds the foreground after that, we LEAVE IT: a window that is+ briefly in front is infinitely better than one that strobes.""" try:- suppress_activation(h)- if _user32.GetForegroundWindow() == h:- _user32.ShowWindow(h, 6) # SW_MINIMIZE -> OS returns focus naturally- _t.sleep(0.05)- _user32.ShowWindow(h, 4) # SW_SHOWNOACTIVATE -> visible again, unfocused+ suppress_activation(h) # style only - no visible change HWND_BOTTOM = 1- SWP = 0x0010 | 0x0002 | 0x0001 # NOACTIVATE|NOMOVE|NOSIZE- _user32.SetWindowPos(h, HWND_BOTTOM, 0, 0, 0, 0, SWP)+ SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE = 0x0010, 0x0002, 0x0001+ _user32.SetWindowPos(h, HWND_BOTTOM, 0, 0, 0, 0,+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE) except Exception: pass @@ -909,8 +916,16 @@ def begin_gui_verb() -> None: def end_gui_verb() -> None:+ """0.9.180: the moment the bridge is idle, hand every window back. A stale+ WS_EX_NOACTIVATE is what makes a taskbar click do nothing (John hit exactly+ that), so it must never outlive the work that justified it.""" global _verb_inflight _verb_inflight = max(0, _verb_inflight - 1)+ if _verb_inflight == 0 and _guard_count == 0:+ try:+ allow_activation_all()+ except Exception:+ pass class _WINDOWPLACEMENT(ctypes.Structure if IS_WINDOWS else object):@@ -990,89 +1005,19 @@ def _ensure_window_icon(hwnd) -> None: pass -def _park_offscreen(hwnd) -> None: # noqa: D401 - clock stamped inside- """Move a window fully off the visible desktop WITHOUT activation.- (Instrumented: attempts + last error surface in focusDebug.)- SetWindowPos silently cannot move a MAXIMIZED window (the -13,-13 no-op,- 2026-08-17), so parking goes through SetWindowPlacement: demote to- normal-size at -32000 with SW_SHOWNOACTIVATE, remembering whether it was- maximized so unpark can honor it."""- global _park_attempts, _park_last_error+def _park_offscreen(hwnd) -> None:+ """RETIRED in 0.9.180 - kept as a no-visible-motion shim.++ Parking used to move the window to (-32000,-32000) and later back, and+ escalated to minimize-first when KiCad fought it. Both are visible frames,+ which is exactly the blinking John reported twice. Backgrounding no longer+ moves windows AT ALL: suppress activation (a style change, invisible) and+ drop to the bottom of the z-order (invisible unless it was on top). Nothing+ to unpark, nothing to restore, nothing to flicker."""+ global _park_attempts+ _park_attempts += 1 _park_since.setdefault(hwnd, __import__("time").monotonic())- try:- _park_attempts += 1- if not _user32.IsWindow(hwnd):- return- r = ctypes.wintypes.RECT()- _user32.GetWindowRect(hwnd, ctypes.byref(r))- if r.left <= -20000:- return # already off-screen- wp = _WINDOWPLACEMENT()- wp.length = ctypes.sizeof(_WINDOWPLACEMENT)- _user32.GetWindowPlacement(hwnd, ctypes.byref(wp))- SW_SHOWMAXIMIZED = 3- was_max = wp.showCmd == SW_SHOWMAXIMIZED- if hwnd not in _parked:- _parked[hwnd] = (r.left, r.top, was_max)- # ESCALATION: an app that keeps reasserting its geometry (eeschema- # during load re-maximizes every ~120ms) turns positional parking into- # visible flicker — after a few fights, minimize it without activation- # instead. wx does not un-minimize itself; the taskbar button still- # works; the screenshot path restores off-screen for capture.- _ensure_window_icon(hwnd) # never freeze a blank icon into the taskbar- _park_fights[hwnd] = _park_fights.get(hwnd, 0) + 1- n = _park_fights[hwnd]-- # STOP FIGHTING (0.9.175). KiCad reasserts its own geometry while it- # loads, so a 50ms sweep that re-parks every time turns into 20-30- # visible blinks - John watched it happen and it reads as broken- # software. We cannot win that argument and should not try: after- # _PARK_FIGHT_LIMIT rounds we concede the POSITION and settle for the- # thing that actually matters - the window stays BEHIND and never takes- # focus. Quiet and slightly visible beats invisible and seizuring.- if n > _PARK_FIGHT_LIMIT:- if hwnd not in _park_gaveup:- _park_gaveup.add(hwnd)- _park_fight_log.append({- "t": __import__("time").time(), "hwnd": int(hwnd), "fights": n,- "action": "conceded position - keeping it behind instead",- })- del _park_fight_log[:-40]- print(f"[park] {hwnd}: conceded after {n} rounds - KiCad keeps "- f"reasserting geometry; leaving it in place, behind, "- f"unfocused", file=sys.stderr, flush=True)- suppress_activation(hwnd)- HWND_BOTTOM = 1- _user32.SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0,- 0x0010 | 0x0001 | 0x0002) # NOACTIVATE|NOSIZE|NOMOVE- _parked.pop(hwnd, None)- _park_since.pop(hwnd, None)- return- if n > 1:- _park_fight_log.append({"t": __import__("time").time(), "hwnd": int(hwnd),- "fights": n, "action": "re-parked"})- del _park_fight_log[:-40]- if _park_fights[hwnd] >= 1: # minimize from first sighting: eeschema- # recreates hwnds during load, resetting any higher threshold, and- # every positional fight round is a visible flicker. Minimize wins- # instantly and wx never un-minimizes itself.- wp.flags = 2 if was_max else 0 # WPF_RESTORETOMAXIMIZED- wp.showCmd = 7 # SW_SHOWMINNOACTIVE- _user32.SetWindowPlacement(hwnd, ctypes.byref(wp))- return- wp.showCmd = SW_SHOWNOACTIVATE # 4- w = max(200, wp.rcNormalPosition.right - wp.rcNormalPosition.left)- h = max(150, wp.rcNormalPosition.bottom - wp.rcNormalPosition.top)- wp.rcNormalPosition.left = -32000- wp.rcNormalPosition.top = -32000- wp.rcNormalPosition.right = -32000 + w- wp.rcNormalPosition.bottom = -32000 + h- wp.flags = 0- ok = _user32.SetWindowPlacement(hwnd, ctypes.byref(wp))- if not ok:- _park_last_error = f"SetWindowPlacement rc=0 lasterr={ctypes.get_last_error()}"- except Exception as e:- _park_last_error = f"{type(e).__name__}: {e}"+ _push_kicad_to_background(hwnd) def _sane_position(hwnd) -> None:@@ -1102,42 +1047,18 @@ def _sane_position(hwnd) -> None: def _unpark(hwnd, to_bottom: bool = True) -> None:+ """0.9.180: nothing is moved any more, so "unparking" only means handing the+ window back to the user - clear the activation block, make sure it has a+ taskbar button and an icon, and leave its geometry exactly where KiCad put+ it. No SetWindowPlacement, no restore, no motion.""" try:- pos = _parked.pop(hwnd, None)+ _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))- wp = _WINDOWPLACEMENT()- wp.length = ctypes.sizeof(_WINDOWPLACEMENT)- _user32.GetWindowPlacement(hwnd, ctypes.byref(wp))- w = max(200, wp.rcNormalPosition.right - wp.rcNormalPosition.left)- h = max(150, wp.rcNormalPosition.bottom - wp.rcNormalPosition.top)- wp.rcNormalPosition.left = x- wp.rcNormalPosition.top = y- wp.rcNormalPosition.right = x + w- wp.rcNormalPosition.bottom = y + h- if was_max and to_bottom:- # can't re-maximize without activation: hand it to the taskbar,- # minimized with restore-to-maximized — the user's click brings it- # back exactly like a normal maximized window (John's contract).- WPF_RESTORETOMAXIMIZED = 2- wp.flags = WPF_RESTORETOMAXIMIZED- wp.showCmd = 7 # SW_SHOWMINNOACTIVE- _user32.SetWindowPlacement(hwnd, ctypes.byref(wp))- else:- wp.flags = 0- wp.showCmd = SW_SHOWNOACTIVATE- _user32.SetWindowPlacement(hwnd, ctypes.byref(wp))- if to_bottom:- HWND_BOTTOM = 1- _user32.SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0,- 0x0010 | 0x0001 | 0x0002) # NOACTIVATE|NOSIZE|NOMOVE- _sane_position(hwnd) # never leave it stranded off-screen _park_since.pop(hwnd, None)- _ensure_window_icon(hwnd) # a fresh button must not read as a blank doc+ if not _user32.IsWindow(hwnd):+ return+ allow_activation(hwnd)+ _ensure_window_icon(hwnd) try: from handlers.win_taskbar import apply_adom_badge apply_adom_badge(hwnd)