← Commit history

v0.9.151: taskbar icon repair - minimize-parking froze windows into the taskbar before KiCad assigned their icon (blank document buttons); parking now stamps iconless windows with their own exe's icon first

John Lauer ·ac1ac92229 ·1mo ago ·parent a8d0179
5 files changed +64−7
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.9.150+0.9.151
adom-bridge-kicad-manifest.json+5−5
@@ -1,15 +1,15 @@ {   "manifest_version": 1,   "name": "kicad",-  "version": "0.9.150",-  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.150/adom-bridge-kicad-v0.9.150.zip",-  "sha256": "9cd704b9ae8027d0e9e1ec6f90f2715542d539a24704aef8b1bd65d02ddd08f9",-  "size": 419952,+  "version": "0.9.151",+  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.151/adom-bridge-kicad-v0.9.151.zip",+  "sha256": "a5e38ba6cfc2b1f15a2d3a5cdbdd633937d6be683ee2c49a3c337647b87fdb38",+  "size": 420496,   "verbPrefixes": [     "kicad_"   ],   "healthEndpoint": "/status",-  "released_at": "2026-08-17T14:39:55Z",+  "released_at": "2026-08-17T15:01:11Z",   "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png",   "languages": [     "Python",
adom-bridge-kicad-v0.9.151.zipadded
⋯ 1 unchanged line ⋯
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "kicad",   "displayName": "KiCad EDA",-  "version": "0.9.150",+  "version": "0.9.151",   "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+57
@@ -479,6 +479,62 @@ class _WINDOWPLACEMENT(ctypes.Structure if IS_WINDOWS else object):                     ("rcNormalPosition", ctypes.wintypes.RECT)]  +_exe_icons: dict = {}  # exe path -> (hicon_big, hicon_small), kept alive+++def _hwnd_exe_path(hwnd) -> str:+    try:+        pid = ctypes.wintypes.DWORD()+        _user32.GetWindowThreadProcessId(hwnd, ctypes.byref(pid))+        h = _kernel32.OpenProcess(0x1000, False, pid.value)+        if not h:+            return ""+        try:+            buf = ctypes.create_unicode_buffer(512)+            size = ctypes.wintypes.DWORD(512)+            if _kernel32.QueryFullProcessImageNameW(h, 0, buf, ctypes.byref(size)):+                return buf.value+        finally:+            _kernel32.CloseHandle(h)+    except Exception:+        pass+    return ""+++def _ensure_window_icon(hwnd) -> None:+    """Minimize-parking freezes a window into the taskbar BEFORE KiCad assigns+    its icon (wx sets it a beat after frame creation), so the shell shows a+    blank document icon (John, 2026-08-17: "why are my kicad icons fucked+    up"). If the window has no icon, stamp it with its own exe's icon."""+    try:+        WM_GETICON, WM_SETICON = 0x007F, 0x0080+        GCLP_HICON, GCLP_HICONSM = -14, -34+        get_cl = getattr(_user32, "GetClassLongPtrW", None) or _user32.GetClassLongW+        has = (_user32.SendMessageW(hwnd, WM_GETICON, 1, 0)+               or _user32.SendMessageW(hwnd, WM_GETICON, 0, 0)+               or get_cl(hwnd, GCLP_HICON) or get_cl(hwnd, GCLP_HICONSM))+        if has:+            return+        exe = _hwnd_exe_path(hwnd)+        if not exe:+            return+        icons = _exe_icons.get(exe)+        if icons is None:+            big = ctypes.wintypes.HICON()+            small = ctypes.wintypes.HICON()+            ctypes.windll.shell32.ExtractIconExW(exe, 0, ctypes.byref(big),+                                                 ctypes.byref(small), 1)+            icons = (big, small)+            _exe_icons[exe] = icons  # keep HICONs alive for process lifetime+        big, small = icons+        if big and big.value:+            _user32.SendMessageW(hwnd, WM_SETICON, 1, big.value)+        if small and small.value:+            _user32.SendMessageW(hwnd, WM_SETICON, 0, small.value)+    except Exception:+        pass++ def _park_offscreen(hwnd) -> None:     """Move a window fully off the visible desktop WITHOUT activation.     (Instrumented: attempts + last error surface in focusDebug.)@@ -507,6 +563,7 @@ def _park_offscreen(hwnd) -> None:         # 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         if _park_fights[hwnd] >= 1:  # minimize from first sighting: eeschema             # recreates hwnds during load, resetting any higher threshold, and