← Commit history

0.9.281: Wiki #71: kicad_show_library failure responses now carry a per-step navigation trace (search Edit found, parent chain, filter text as read back, tree candidates with class and rect, editor title after Down, after posted Enter, after posted double-click). Ray measured on his box that the tree lists the symbol and a real Enter loads it while the bridge's posted Enter and double-click do not; the trace is how the next build finds which hwnd the Enter must reach.

John Lauer ·15ac680cc1 ·1mo ago ·parent eed9703
1 file changed +65−1
handlers/open_symbol_editor.py+65−1
@@ -441,6 +441,41 @@ def _plugin_background_load(symbol_name: str, library_name: str = "",         return {"loaded": False, "_error": f"{type(e).__name__}: {e}"}  +# wiki #71 (2026-09-03): the background navigation failed on a CLEAN library on+# ConfRoomROG and on Ray's box with no measurement of WHICH step missed. Every+# _background_navigate_win32 run now records its steps here; the failing+# show_library response carries the last run as `navigation`.+_LAST_NAV_TRACE: dict = {}+++def _win_class(hwnd: int) -> str:+    try:+        buf = ctypes.create_unicode_buffer(128)+        user32.GetClassNameW(hwnd, buf, 128)+        return buf.value+    except Exception:  # pylint: disable=broad-except+        return "?"+++def _win_rect(hwnd: int) -> list:+    try:+        r = ctypes.wintypes.RECT()+        user32.GetWindowRect(hwnd, ctypes.byref(r))+        return [r.left, r.top, r.right, r.bottom]+    except Exception:  # pylint: disable=broad-except+        return []+++def _edit_text(hwnd: int) -> str:+    try:+        n = user32.GetWindowTextLengthW(hwnd)+        buf = ctypes.create_unicode_buffer(n + 1)+        user32.GetWindowTextW(hwnd, buf, n + 1)+        return buf.value+    except Exception:  # pylint: disable=broad-except+        return "?"++ def _background_navigate_win32(sym_hwnd: int, filter_text: str, verify: str,                                verify_lib: str = "") -> bool:     """v0.9.83 (#20/#21): plugin-free BACKGROUND symbol load. WM_SETTEXT into the@@ -450,22 +485,42 @@ def _background_navigate_win32(sym_hwnd: int, filter_text: str, verify: str,     no in-process plugin needed — works even while eeschema's embedded Python is     still minutes from initializing (issue #21).     Returns True when the editor title confirms `verify` loaded."""+    global _LAST_NAV_TRACE+    trace: dict = {"filter": filter_text, "verify": verify, "editorHwnd": int(sym_hwnd),+                   "titleBefore": _get_symbol_editor_title(), "steps": []}+    _LAST_NAV_TRACE = trace     edit = _find_child_by_class_and_parent(sym_hwnd, "Edit", "searchCtrl")     if not edit:+        trace["steps"].append("search Edit under a 'searchCtrl' parent NOT FOUND (KiCad 10 child window layout?)")         return False+    trace["editHwnd"] = int(edit)+    trace["editParentChain"] = []+    try:+        h = edit+        for _ in range(4):+            h = user32.GetParent(h)+            if not h:+                break+            trace["editParentChain"].append({"hwnd": int(h), "class": _win_class(h), "text": _edit_text(h)[:40]})+    except Exception:  # pylint: disable=broad-except+        pass     WM_SETTEXT = 0x000C     WM_KEYDOWN, WM_KEYUP, VK_RETURN = 0x0100, 0x0101, 0x0D     try:         user32.SendMessageW(edit, WM_SETTEXT, 0, ctypes.create_unicode_buffer(filter_text))     except Exception:+        trace["steps"].append("WM_SETTEXT raised")         return False     time.sleep(1.4)  # LIB_TREE search debounce runs on the UI thread (~200-500ms) + tree rebuild+    trace["editTextAfterSet"] = _edit_text(edit)+    trace["steps"].append(f"WM_SETTEXT -> edit reads {trace['editTextAfterSet']!r}")     VK_DOWN = 0x28     # Down THROUGH the filter box: KiCad forwards nav keys to the tree, which     # selects the first filtered match (verified live: Device:R row highlights).     user32.PostMessageW(edit, WM_KEYDOWN, VK_DOWN, 0)     user32.PostMessageW(edit, WM_KEYUP, VK_DOWN, 0)     time.sleep(0.5)+    trace["steps"].append(f"VK_DOWN posted to edit; title now {_get_symbol_editor_title()!r}")      def _tree_candidates():         """The LIB_TREE dataview lives near the searchCtrl: collect tall sibling@@ -497,13 +552,17 @@ def _background_navigate_win32(sym_hwnd: int, filter_text: str, verify: str,      # Round 1: Enter posted straight to each tree candidate (the payload's own     # in-process technique, aimed from outside).-    for hwnd, _r in _tree_candidates():+    cands = _tree_candidates()+    trace["treeCandidates"] = [{"hwnd": int(h), "class": _win_class(h), "rect": [r.left, r.top, r.right, r.bottom]} for h, r in cands]+    for hwnd, _r in cands:         user32.PostMessageW(hwnd, WM_KEYDOWN, VK_RETURN, 0)         user32.PostMessageW(hwnd, WM_KEYUP, VK_RETURN, 0)     for _ in range(6):         time.sleep(0.7)         if _title_ok():+            trace["steps"].append("VK_RETURN to tree candidates -> title confirms")             return True+    trace["steps"].append(f"VK_RETURN to {len(cands)} tree candidate(s); title still {_get_symbol_editor_title()!r}")      # Round 2: double-click the selected row (first visible row sits just under     # the header) — pure PostMessage, still zero focus.@@ -519,7 +578,10 @@ def _background_navigate_win32(sym_hwnd: int, filter_text: str, verify: str,     for _ in range(8):         time.sleep(0.7)         if _title_ok():+            trace["steps"].append("posted double-click on first row -> title confirms")             return True+    trace["steps"].append(f"posted double-click on first row; title still {_get_symbol_editor_title()!r}")+    trace["editTextAtEnd"] = _edit_text(edit)     return False  @@ -1129,10 +1191,12 @@ def handle_open_symbol_editor(kicad_info: dict, args: dict) -> dict:                 # a OneDrive-redirected box the bridge had been writing elsewhere.                 # So report the paths, measured, and say only what they show.                 **_stale_explanation(kicad_info, search_text),+                "navigation": dict(_LAST_NAV_TRACE),             }         return {             "success": False, "editorOpened": True, "symbolLoaded": False,             "attempts": 2,+            "navigation": dict(_LAST_NAV_TRACE),             "error": f"Symbol Editor open but '{search_text}' didn't load in the background "                      f"after two attempts (title: {_get_symbol_editor_title() or 'unknown'})",             "_hint": ("Two background attempts did not confirm, so a third is unlikely to "