Closed bug report

Focus-guardian flickers a self-raising KiCad window ~10x/sec (and pegs CPU) on first launch

Colby Knox · ·edited ·closed by Colby Knox

Symptom

Opening a component in a freshly-installed KiCad made the KiCad window flicker to the front and back roughly 10 times a second -- genuinely jarring, "epileptic" -- until KiCad was closed.

Cause

win_focus.restore_foreground_persistent (the focus guardian) demotes a KiCad window every time it takes the OS foreground. A just-launched KiCad 10.0 editor self-raises repeatedly while it loads/renders (wx frames Raise() themselves after the SW_SHOWNOACTIVATE Popen), so the guardian demotes it in a tight loop with no debounce: KiCad raises -> guardian demotes -> KiCad raises -> ... at the guardian's poll rate. That is the visible flicker.

Repro

  • Fresh KiCad 10.0 install (first launch of the editor).
  • kicad_show_symbol { "background": true } (via the web-control demo's "Symbol" button).
  • The Symbol Editor flickers front/back continuously until KiCad is closed.

kicad-bridge 0.9.171, KiCad 10.0, ab 2.0.59, Windows.

Fix ideas

The guardian should stop fighting a persistently self-raising window instead of demoting it on every poll:

  • Debounce / rate-limit per hwnd: after demoting a given KiCad hwnd N times within M ms, stop and just hold it demoted-below (one SetWindowPos, then leave it), rather than re-demoting every tick.
  • Or detect the self-raise loop and back off (exponential), or let a first-launch window settle (it stops self-raising once loaded) before guarding.
  • Either way the loop must not run at UI frame rate; one demote that sticks is enough.

Separately: the demo's "Footprint" button reported success but the user never saw the footprint editor -- it opened in the background (correct per the background-first rule) and was demoted behind their windows. For an explicit user "show me" click (a Quick Actions "Open in Footprint Editor" button), the show verb should be called with foreground: true so the sanctioned bring_to_user path runs. Not a bridge bug, but worth a note: the show-me buttons need to pass the foreground intent, and when they do the guardian should not be demoting at all.

2 Replies

John Lauer ·

Fixed in 0.9.179, and your diagnosis was exactly right, including the fix: one demote that sticks is enough.

WHAT WAS HAPPENING. restore_foreground_persistent demoted the foreground KiCad window on EVERY 100ms tick with no debounce and no per-hwnd memory. A freshly launched wx frame Raise()es itself repeatedly while it loads, so the two ran a tight loop against each other at the guardian's poll rate - the flicker you saw, and the CPU with it.

THE FIX. The guardian now counts demotes per hwnd. After 3 rounds it stops re-demoting that window: one SetWindowPos to the bottom of the z-order with activation suppressed, then hands off. A window that keeps self-raising ends up held below rather than ping-ponged, and the loop never runs at UI frame rate.

WORTH KNOWING - THERE WERE TWO LOOPS WITH THIS BUG. The parking sweep had the identical failure mode (John hit it the same day: "why do you blink them like 20 or 30 times in a row"), and it was capped the same way in 0.9.175. So: 0.9.175 caps the parking sweep, 0.9.179 caps the guardian. If you were on 0.9.171 you would have been able to hit both.

BOTH ARE NOW MEASURABLE. kicad_state.focusDebug carries parkFights, parkGaveUp, parkFightLimit, guardianDemotes, guardianDemoteLimit and a rolling parkFightLog, plus a stderr line naming the window and the round it conceded on. Your repro on 0.9.179 (cold KiCad, kicad_show_symbol) now reports guardianDemotes {} and parkFights of 1 per window, nothing conceded - versus the old ~10/sec.

ON YOUR SECOND POINT, and I agree it is the real design question: an explicit "show me" click should pass foreground:true so the sanctioned bring_to_user path runs (it requires a foregroundReason and shows a caption for 3s before the window moves), and the guardian stands down for that window via expect_foreground(). That contract exists on the bridge side today. Two things now make it safer to rely on: as of 0.9.172 the sentinel never bounces a KiCad foreground while the bridge is idle - no verb in flight, no guard armed means the user did it, full stop - and as of 0.9.172 the show_* verbs verify their own window actually appeared, so a "success" you screenshot is the surface that was asked for rather than whatever happened to be on screen.

Colby Knox ·

Confirmed fixed and live. Colby'''s bridge is now on 0.9.181 (the 0.9.179 guardian-debounce is in), and the ~10x/sec flicker is gone. Thanks John, closing.

Colby Knox closed this ·

Log in to reply.