← Commit history

v0.9.149: fight escalation - a window reasserting its geometry (eeschema mid-load) gets minimized-no-activate after 4 repark fights (flicker-free); screenshots restore park-minimized windows off-screen for capture

John Lauer ·3b8871c4d4 ·1mo ago ·parent e9e6a76
6 files changed +44−9
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.9.148+0.9.149
adom-bridge-kicad-manifest.json+5−5
@@ -1,15 +1,15 @@ {   "manifest_version": 1,   "name": "kicad",-  "version": "0.9.148",-  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.148/adom-bridge-kicad-v0.9.148.zip",-  "sha256": "e8cbef9b98554667ef37777118316f6ca38508ce2a6edc3805fd9c5beda8c421",-  "size": 419322,+  "version": "0.9.149",+  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.149/adom-bridge-kicad-v0.9.149.zip",+  "sha256": "c7a6c63fb953b57cb1e014661b2ce2e31dea3cc896dc7e872647146fc34f45e3",+  "size": 419858,   "verbPrefixes": [     "kicad_"   ],   "healthEndpoint": "/status",-  "released_at": "2026-08-17T14:36:47Z",+  "released_at": "2026-08-17T14:38:31Z",   "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png",   "languages": [     "Python",
adom-bridge-kicad-v0.9.149.zipadded
⋯ 1 unchanged line ⋯
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "kicad",   "displayName": "KiCad EDA",-  "version": "0.9.148",+  "version": "0.9.149",   "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+21
@@ -267,6 +267,27 @@ def _write_bmp(path: str, width: int, height: int, pixel_data: bytes):  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 —+    # restore it OFF-SCREEN without activation just for the capture.+    try:+        if user32.IsIconic(hwnd):+            from handlers.win_focus import _park_offscreen, _WINDOWPLACEMENT+            import ctypes as _ct+            wp = _WINDOWPLACEMENT()+            wp.length = _ct.sizeof(_WINDOWPLACEMENT)+            user32.GetWindowPlacement(hwnd, _ct.byref(wp))+            w = max(400, wp.rcNormalPosition.right - wp.rcNormalPosition.left)+            h = max(300, 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+            wp.showCmd = 4  # SW_SHOWNOACTIVATE+            user32.SetWindowPlacement(hwnd, _ct.byref(wp))+            time.sleep(0.3)+    except Exception:+        pass     rect = _get_window_rect(hwnd)     width = rect["width"]     height = rect["height"]
handlers/win_focus.py+16−2
@@ -359,7 +359,8 @@ def get_focus_events() -> list: # AT THE BOTTOM of the z-order. Taskbar buttons keep working; PrintWindow # capture works off-screen; the user sees nothing appear. # ----------------------------------------------------------------------------_parked: dict = {}  # hwnd -> (x, y)+_parked: dict = {}  # hwnd -> (x, y, was_max)+_park_fights: dict = {}  # hwnd -> repark count _park_attempts = 0 _park_last_error = "" _sweep_ticks = 0@@ -427,7 +428,8 @@ def _start_park_sweep() -> None:                         quiet_since = None                     else:                         for h in list(_parked.keys()):-                            _park_offscreen(h)+                            if not _user32.IsIconic(h):+                                _park_offscreen(h)                         _t.sleep(0.05)                         continue                     _t.sleep(0.1)@@ -500,6 +502,17 @@ def _park_offscreen(hwnd) -> None:         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.+        _park_fights[hwnd] = _park_fights.get(hwnd, 0) + 1+        if _park_fights[hwnd] >= 4:+            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)@@ -518,6 +531,7 @@ def _park_offscreen(hwnd) -> None: def _unpark(hwnd, to_bottom: bool = True) -> None:     try:         pos = _parked.pop(hwnd, None)+        _park_fights.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))