← Commit history

v0.9.115: sentinel bless test fixed - 'recent input' wrongly blessed self-raises while the user typed in ANOTHER app (caught live); now only a click landing ON the window or an in-progress alt-tab counts as user intent

John Lauer ·32e9e7d4b5 ·2mo ago ·parent e230a5a
5 files changed +40−14
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.9.114+0.9.115
adom-bridge-kicad-manifest.json+5−5
@@ -1,15 +1,15 @@ {   "manifest_version": 1,   "name": "kicad",-  "version": "0.9.114",-  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.114/adom-bridge-kicad-v0.9.114.zip",-  "sha256": "4aafd591a26ff321f3ba3242a03d7a0db35664971552b96104db0e8899a297a0",-  "size": 408981,+  "version": "0.9.115",+  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.115/adom-bridge-kicad-v0.9.115.zip",+  "sha256": "733b6ddad46b92a6ec97fc74909ca917ea63f7b610269c90209d687966d6af87",+  "size": 409411,   "verbPrefixes": [     "kicad_"   ],   "healthEndpoint": "/status",-  "released_at": "2026-08-16T13:25:01Z",+  "released_at": "2026-08-16T13:29:16Z",   "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png",   "languages": [     "Python",
adom-bridge-kicad-v0.9.115.zipadded
⋯ 1 unchanged line ⋯
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "kicad",   "displayName": "KiCad EDA",-  "version": "0.9.114",+  "version": "0.9.115",   "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+33−7
@@ -338,20 +338,48 @@ def start_focus_sentinel() -> None:         except Exception:             pass +    def _cursor_over(hwnd) -> bool:+        """Is the cursor currently over this window (top-level hit test)?"""+        try:+            import ctypes as _ct+            pt = _ct.wintypes.POINT()+            _user32.GetCursorPos(_ct.byref(pt))+            under = _user32.WindowFromPoint(pt)+            GA_ROOT = 2+            return bool(under) and _user32.GetAncestor(under, GA_ROOT) == hwnd+        except Exception:+            return False+     def _loop():+        # v0.9.115: "recent user input" was the wrong bless test — the user+        # typing in ANOTHER app is exactly when a self-raise is most disruptive,+        # yet it counted as user intent (caught live by John over RustDesk).+        # A foreground is user-initiated only if a mouse CLICK landed (and the+        # cursor sits on the new window) or an alt-tab was in progress.         last_fg = None-        blessed = set()  # KiCad hwnds the user deliberately foregrounded+        last_click_t = 0.0+        last_alt_t = 0.0+        VK_LBUTTON, VK_RBUTTON, VK_MENU = 0x01, 0x02, 0x12         while True:             try:+                now = _t.monotonic()+                try:+                    if (_user32.GetAsyncKeyState(VK_LBUTTON) & 0x8000) or \+                       (_user32.GetAsyncKeyState(VK_RBUTTON) & 0x8000):+                        last_click_t = now+                    if _user32.GetAsyncKeyState(VK_MENU) & 0x8000:+                        last_alt_t = now+                except Exception:+                    pass                 fg = _user32.GetForegroundWindow()                 if fg != last_fg:                     if fg and is_kicad_window(fg):                         ago = _seconds_since_user_input()-                        if _t.monotonic() < _expected_fg_until:-                            blessed.add(fg)+                        clicked_here = (now - last_click_t) <= 1.2 and _cursor_over(fg)+                        alt_tabbed = (now - last_alt_t) <= 2.0+                        if now < _expected_fg_until:                             _log("sanctioned-foreground (allowed)", fg, ago)-                        elif ago <= _USER_INTENT_WINDOW_S:-                            blessed.add(fg)+                        elif clicked_here or alt_tabbed:                             _log("user-foregrounded (left alone)", fg, ago)                         elif _guard_count > 0:                             _log("steal-during-guard (guardian handles)", fg, ago)@@ -361,8 +389,6 @@ def start_focus_sentinel() -> None:                             # no guard is active: immediately clear the NOACTIVATE                             # bit the bounce set, so a real user click still works                             allow_activation(fg)-                    else:-                        blessed.discard(last_fg)                     last_fg = fg                 _t.sleep(0.12)             except Exception: