From 190e28b9ecc6f357716cc896d5d7489879c9278d Mon Sep 17 00:00:00 2001 From: blasty Date: Sat, 25 Jul 2026 20:46:19 +0200 Subject: split: keep _active in step with focus, so Enter follows the pane you're in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported as "Esc in the decompiler needs two presses". It was never Esc. In split mode Tab sets _active AND focus together, but focus also moves on its own — a click, or a pane focusing itself after a load — and _active was left naming the pane you are NOT in. Everything downstream trusts _active, so with the pseudocode on screen and the listing still "active", Enter resolved the word under the LISTING's cursor: it followed something unrelated (typically the outer function itself, landing on line 0 col 19 — its own name in the prototype) and pushed history for it. The first Esc was then spent undoing that bogus entry and looked like a no-op; the second did what you meant. on_descendant_focus now syncs _active to the focused code view while split, the same pair of steps Tab already does (set _active, re-link the band, restatus). Verified on a live pane driven through tmux, which is the only harness in this session that told the truth — the in-process pilot never exercised follow-then-Esc in split, which is why four earlier attempts "passed" while the bug sat there: before: Enter on sub_3500 -> fn main, L0 (never left the function) after: Enter on sub_3500 -> fn sub_3500, L0 then Esc -> main L54, one press Suite 192/0. Known remainder, deliberately not fixed here: a follow still pushes two entries (nav 2 -> 4), so the stack is deeper than it should be. Landing is correct at every step, so it isn't visible as the reported bug. The obvious dedupe in _open_decomp_entry (skip appending src when the top entry is the same place) is inert — the duplicate comes from somewhere else, and I'd rather leave it open than commit a fix I can't show working. --- idatui/app.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/idatui/app.py b/idatui/app.py index 57ec7df..5889a27 100644 --- a/idatui/app.py +++ b/idatui/app.py @@ -3879,6 +3879,27 @@ class IdaTui(App): return self._goto_ea(addr, push=True) # land on the literal in the listing + def on_descendant_focus(self, event) -> None: # type: ignore[no-untyped-def] + """Keep ``_active`` in step with focus while split. + + Tab moves both together, but focus also moves on its own — a click, or a + pane focusing itself after a load — and then ``_active`` still names the + pane you're NOT in. Everything downstream trusts ``_active``: follow + resolves the word under that pane's cursor and pushes history for it, so + Enter in the pseudocode would follow something from the listing and the + next Esc got spent undoing it. + """ + if not self._split: + return + w = self.focused + mode = ("decomp" if isinstance(w, DecompView) + else "listing" if isinstance(w, ListingView) else None) + if mode is None or mode == self._active: + return + self._active = mode + self._sync_split(mode) # re-link the band from the new driver + self._status_for_cur("split") + def action_toggle_view(self) -> None: """Tab: switch the code pane between disassembly and pseudocode (or leave the hex view back to the preferred code view).""" -- cgit v1.3.1-sl0p