diff options
| author | blasty <blasty@local> | 2026-08-07 00:07:22 +0200 |
|---|---|---|
| committer | blasty <blasty@local> | 2026-08-07 00:07:22 +0200 |
| commit | cefdeb88ab3271313db741c9c95677178b18d484 (patch) | |
| tree | 4c44ffda644a0ca0f891b17d61d889e10114083b /idatui/drive.py | |
| parent | tests: a guard that actually regresses on the resync storm (diff) | |
| download | ida-tui-cefdeb88ab3271313db741c9c95677178b18d484.tar.gz ida-tui-cefdeb88ab3271313db741c9c95677178b18d484.tar.xz ida-tui-cefdeb88ab3271313db741c9c95677178b18d484.zip | |
app: the view mode is a type, and 'disasm' is gone
_active was a bare string with 49 comparisons across four modules and a fifth
value nobody meant to keep. "disasm" was assigned on exactly one path -- a
decompile that failed with nowhere to return to -- and named the same widget as
"listing". Four sites understood it; five compared against "listing" alone and
silently took the wrong branch:
* Tab out of a failed decompile set "listing" instead of "decomp", so the
first press appeared to do nothing.
* rpc.py carried a workaround for a mode change that never arrived, keyed on
being ALREADY in the ghost state -- so it fired in the rare case and not in
the common one. Now keyed on LISTING, which is the case that happens.
* drive.py asked the socket to show it "disasm", a value the app will now
never report, and would have toggled twice and given up.
ViewMode is a StrEnum on purpose: _active goes straight to drivers as
cursor.kind and the pilot compares it to plain strings, so members being strings
keeps every payload and comparison working. What it buys is one place that says
which modes exist, and an AttributeError instead of silence on a typo.
Read it through is_listing/is_decomp/is_hex/is_graph/in_code rather than ==.
The bare comparisons are what let the ghost hide, and they are what the next
mode would have to hunt down -- adding "graph" already cost one crash that way
(_active_code_view returning None when a prompt closed).
view_modes_all_handled walks the enum and asks the app the questions it asks
itself. Verified it bites: adding a fifth unhandled member fails it twice.
746 checks, 142.3s.
Diffstat (limited to 'idatui/drive.py')
| -rw-r--r-- | idatui/drive.py | 10 |
1 files changed, 5 insertions, 5 deletions
diff --git a/idatui/drive.py b/idatui/drive.py index b83a8ba..1c7d8c2 100644 --- a/idatui/drive.py +++ b/idatui/drive.py @@ -64,7 +64,7 @@ def _fmt_where(st: dict) -> str: ea = fn.get("ea") loc = f"{name} @ {ea:#x}" if isinstance(ea, int) else "(none)" extra = "" - if cur.get("kind") in ("decomp", "disasm"): + if cur.get("kind") in ("decomp", "listing"): extra = f" L{cur.get('line')} C{cur.get('col')} word={cur.get('word')!r}" elif cur.get("kind") == "hex": extra = f" va={cur.get('va'):#x}" if isinstance(cur.get("va"), int) else "" @@ -87,10 +87,10 @@ def cmd_go(c, args): def _show_view(c, want): """Make the requested code pane the visibly-active view (best effort). - Tab toggles disasm<->decomp, and leaves hex back to the preferred code + Tab toggles listing<->decomp, and leaves hex back to the preferred code view; so at most two toggles reach either code view from any state. If - the decompiler fails for the current function the view falls back to - disasm and we simply stop — the caller still returns its text as before. + the decompiler fails for the current function the view falls back to the + listing and we simply stop — the caller still returns its text as before. """ for _ in range(2): if c.call("state").get("active") == want: @@ -132,7 +132,7 @@ def cmd_dis(c, args): # Drive the real UI so viewers see the disassembly, not just the driver. if target is not None: c.call("goto", target=target, delay_ms=0) - _show_view(c, "disasm") + _show_view(c, "listing") d = c.call("disassembly", target=target, max=n) return "\n".join(f"{ln['ea']:#010x} {ln['text']}" for ln in d.get("lines", [])) |
