← Commit history

v0.9.141: 50ms parking sweep (deterministic) - baselines existing windows at verb start, parks anything newer off-screen while verbs/guards are live; complements the SHOW hook which arms but gets no events on some sessions

John Lauer ·2532cf24f8 ·1mo ago ·parent d02d8ac
5 files changed +71−9
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.9.140+0.9.141
adom-bridge-kicad-manifest.json+5−5
@@ -1,15 +1,15 @@ {   "manifest_version": 1,   "name": "kicad",-  "version": "0.9.140",-  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.140/adom-bridge-kicad-v0.9.140.zip",-  "sha256": "ededb375e362caeef7b4db7baad7ad3f3622ecc1f60bd7d4225556d6e6977e66",-  "size": 417463,+  "version": "0.9.141",+  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.141/adom-bridge-kicad-v0.9.141.zip",+  "sha256": "4c30eb514b5cb3df2890750a99bd62c702f932f2071decdd4309617483c58b2b",+  "size": 417817,   "verbPrefixes": [     "kicad_"   ],   "healthEndpoint": "/status",-  "released_at": "2026-08-17T14:22:15Z",+  "released_at": "2026-08-17T14:23:57Z",   "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png",   "languages": [     "Python",
adom-bridge-kicad-v0.9.141.zipadded
⋯ 1 unchanged line ⋯
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "kicad",   "displayName": "KiCad EDA",-  "version": "0.9.140",+  "version": "0.9.141",   "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+64−2
@@ -346,11 +346,73 @@ _parked: dict = {}  # hwnd -> (x, y) _verb_inflight = 0  +_park_baseline: set = set()+_park_sweep_started = False+++def _enum_kicad_hwnds() -> set:+    out = set()+    try:+        WNDENUMPROC = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.wintypes.HWND,+                                         ctypes.wintypes.LPARAM)+        def cb(h, _l):+            try:+                if _user32.IsWindowVisible(h) and is_kicad_window(h):+                    out.add(h)+            except Exception:+                pass+            return True+        cbp = WNDENUMPROC(cb)+        _user32.EnumWindows(cbp, 0)+    except Exception:+        pass+    return out+++def _start_park_sweep() -> None:+    """50ms parking sweep, active only while a GUI verb is in flight or a guard+    is armed: any KiCad window NOT in the verb-start baseline is suppressed and+    parked off-screen the moment it enumerates. Deterministic complement to the+    SHOW hook (which arms but receives no events on some sessions)."""+    global _park_sweep_started+    if _park_sweep_started or not IS_WINDOWS:+        return+    _park_sweep_started = True+    import threading+    import time as _t++    def _loop():+        while True:+            try:+                if _verb_inflight > 0 or _guard_count > 0:+                    if _t.monotonic() >= _expected_fg_until:+                        for h in _enum_kicad_hwnds() - _park_baseline:+                            suppress_activation(h)+                            _park_offscreen(h)+                    _t.sleep(0.05)+                else:+                    _t.sleep(0.25)+            except Exception:+                try:+                    _t.sleep(0.5)+                except Exception:+                    pass++    try:+        threading.Thread(target=_loop, daemon=True, name="kicad-park-sweep").start()+    except Exception:+        pass++ def begin_gui_verb() -> None:-    """Dispatcher marks a GUI verb in flight so the SHOW hook parks windows-    created DURING the handler (guards only arm after it returns)."""+    """Dispatcher marks a GUI verb in flight; windows existing NOW are the+    user's (baseline) — anything newer gets parked by the 50ms sweep."""     global _verb_inflight+    if _verb_inflight == 0 and _guard_count == 0:+        _park_baseline.clear()+        _park_baseline.update(_enum_kicad_hwnds() - set(_parked.keys()))     _verb_inflight += 1+    _start_park_sweep()   def end_gui_verb() -> None: