← Commit history

v0.9.143: SetWindowPlacement-based parking - SetWindowPos cannot move a MAXIMIZED window (the silent no-op); park demotes to normal off-screen, unpark restores true coords bottom-z, ex-maximized windows go to the taskbar minimized with restore-to-maximized

John Lauer ·47ae756075 ·1mo ago ·parent 49142e8
5 files changed +63−19
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.9.142+0.9.143
adom-bridge-kicad-manifest.json+5−5
@@ -1,15 +1,15 @@ {   "manifest_version": 1,   "name": "kicad",-  "version": "0.9.142",-  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.142/adom-bridge-kicad-v0.9.142.zip",-  "sha256": "eace7f86a7bce47c92bdf7d1eab73e80222ce7b847048f07ef1a2226bc3f815a",-  "size": 417950,+  "version": "0.9.143",+  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.143/adom-bridge-kicad-v0.9.143.zip",+  "sha256": "18bd5164feeead23309821859a354235b9e4e231d16d6720392e182ab675a775",+  "size": 418363,   "verbPrefixes": [     "kicad_"   ],   "healthEndpoint": "/status",-  "released_at": "2026-08-17T14:25:17Z",+  "released_at": "2026-08-17T14:27:48Z",   "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png",   "languages": [     "Python",
adom-bridge-kicad-v0.9.143.zipadded
⋯ 1 unchanged line ⋯
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "kicad",   "displayName": "KiCad EDA",-  "version": "0.9.142",+  "version": "0.9.143",   "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+56−12
@@ -420,7 +420,22 @@ def end_gui_verb() -> None:     _verb_inflight = max(0, _verb_inflight - 1)  +class _WINDOWPLACEMENT(ctypes.Structure if IS_WINDOWS else object):+    if IS_WINDOWS:+        _fields_ = [("length", ctypes.wintypes.UINT),+                    ("flags", ctypes.wintypes.UINT),+                    ("showCmd", ctypes.wintypes.UINT),+                    ("ptMinPosition", ctypes.wintypes.POINT),+                    ("ptMaxPosition", ctypes.wintypes.POINT),+                    ("rcNormalPosition", ctypes.wintypes.RECT)]++ def _park_offscreen(hwnd) -> None:+    """Move a window fully off the visible desktop WITHOUT activation.+    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."""     try:         if not _user32.IsWindow(hwnd):             return@@ -428,14 +443,22 @@ def _park_offscreen(hwnd) -> None:         _user32.GetWindowRect(hwnd, ctypes.byref(r))         if r.left <= -20000:             return  # already off-screen-        # KiCad applies its SAVED geometry late in init, moving itself back-        # on-screen AFTER an early park — so RE-park every time we see it-        # visible, but only record the true coordinates once (the first-        # sighting; later sightings are just KiCad fighting the park).+        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)-        SWP = 0x0010 | 0x0001 | 0x0004  # NOACTIVATE|NOSIZE|NOZORDER-        _user32.SetWindowPos(hwnd, 0, -32000, -32000, 0, 0, SWP)+            _parked[hwnd] = (r.left, r.top, was_max)+        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+        _user32.SetWindowPlacement(hwnd, ctypes.byref(wp))     except Exception:         pass @@ -445,11 +468,32 @@ def _unpark(hwnd, to_bottom: bool = True) -> None:         pos = _parked.pop(hwnd, None)         if pos is None or not _user32.IsWindow(hwnd):             return-        HWND_BOTTOM = 1-        SWP = 0x0010 | 0x0001  # NOACTIVATE|NOSIZE-        _user32.SetWindowPos(hwnd, HWND_BOTTOM if to_bottom else 0,-                             pos[0], pos[1], 0, 0,-                             SWP | (0 if to_bottom else 0x0004))+        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     except Exception:         pass