<feed xmlns='http://www.w3.org/2005/Atom'>
<title>ida-tui.git/idatui/app.py, branch main</title>
<subtitle>tui for headless ida</subtitle>
<id>https://git.sl0p.foo/ida-tui.git/atom/idatui/app.py?h=main</id>
<link rel='self' href='https://git.sl0p.foo/ida-tui.git/atom/idatui/app.py?h=main'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/ida-tui.git/'/>
<updated>2026-07-11T20:58:52Z</updated>
<entry>
<title>disasm: render opcode bytes (toggle 'o'), padded to widest insn</title>
<updated>2026-07-11T20:58:52Z</updated>
<author>
<name>blasty</name>
<email>blasty@local</email>
</author>
<published>2026-07-11T20:58:52Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/ida-tui.git/commit/?id=17b8c95b948d4fe2d38cac4f80ffa8fb0a750d4f'/>
<id>urn:sha1:17b8c95b948d4fe2d38cac4f80ffa8fb0a750d4f</id>
<content type='text'>
Fetch per-instruction opcode bytes alongside the disasm listing and show
them in a column between the address and the mnemonic. Instruction length
comes from consecutive addresses (variable-length safe: x86 movabs shows
its full 10 bytes); the block over-fetches one instruction for the last
line's boundary, falling back to the function end for the final insn.

The column pads to the widest instruction across the whole function:
_fetch_block tracks a running max, and on load a background scan_bytes()
settles a stable width so padding doesn't jump as blocks stream in.
_op_field is shared by the rendered strip, _line_plain, and search _fmt
so cursor/match offsets stay aligned. 'o' toggles the column.
</content>
</entry>
<entry>
<title>app: multi-line comments via literal \\n (long notes were clipping)</title>
<updated>2026-07-10T14:43:32Z</updated>
<author>
<name>blasty</name>
<email>blasty@local</email>
</author>
<published>2026-07-10T14:43:32Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/ida-tui.git/commit/?id=7ae37ba3b432bbb2482df9c99864350e685b0241'/>
<id>urn:sha1:7ae37ba3b432bbb2482df9c99864350e685b0241</id>
<content type='text'>
The comment prompt is single-line, so a long note rendered as one giant
'//' line that ran off the right edge and got clipped. Interpret a literal
'\\n' (backslash-n) in the comment text as a real newline in _do_comment
(the single choke point for the ';' prompt and the RPC 'comment' verb) —
Hex-Rays renders each as its own '//' line. Verified live and locked in
the comment_func scenario (multi-line render); suite 104 green.
</content>
</entry>
<entry>
<title>app: comment on a no-address line -&gt; function comment (was refused)</title>
<updated>2026-07-10T14:12:22Z</updated>
<author>
<name>blasty</name>
<email>blasty@local</email>
</author>
<published>2026-07-10T14:12:22Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/ida-tui.git/commit/?id=3b4e397f48e4519c6b5a24fede2e41707e355a92'/>
<id>urn:sha1:3b4e397f48e4519c6b5a24fede2e41707e355a92</id>
<content type='text'>
Pressing ';' on the decompiler's signature or local-declaration lines did
nothing ('no address on this line to comment') because those lines carry
no /*0xEA*/ marker. Fall back to the current function's entry ea so
commenting the header annotates the function itself; the prompt says
'function comment @ ...'. Body-line comments are unchanged.

New scenario comment_func locks it (suite 104 green).
</content>
</entry>
<entry>
<title>app: drop the Header (keep the Footer); rpc: function-scope xrefs_from</title>
<updated>2026-07-10T14:01:54Z</updated>
<author>
<name>blasty</name>
<email>blasty@local</email>
</author>
<published>2026-07-10T14:01:54Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/ida-tui.git/commit/?id=ead2125f041649024ede36d0b4eb8e4c4c466e32'/>
<id>urn:sha1:ead2125f041649024ede36d0b4eb8e4c4c466e32</id>
<content type='text'>
The clock/title Header added noise (and made screen() non-deterministic);
remove it from the app itself — the Footer stays. Layout shifts up a row;
scenario suite still 101 green.

xrefs_from on a function was near-useless: the server's xrefs_from is
address-scoped, so passing the entry ea only returned the fall-through
from the first instruction. For a function target it now returns
whole-body references from the decompiler (callees + string/data refs:
{to,name,string,is_func,type}); an explicit 0xADDR stays address-scoped.
Verified live: xrefs_from main now lists setlocale/getopt_long/sub_* etc.
rpc_smoke: 25 green.
</content>
</entry>
<entry>
<title>rpc: unix-socket puppeteering server (raw keys + introspection + screen)</title>
<updated>2026-07-10T13:10:25Z</updated>
<author>
<name>blasty</name>
<email>blasty@local</email>
</author>
<published>2026-07-10T13:10:25Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/ida-tui.git/commit/?id=690a34512eaed933917f2d2c1cf12327923b39ac'/>
<id>urn:sha1:690a34512eaed933917f2d2c1cf12327923b39ac</id>
<content type='text'>
Run the TUI with --rpc &lt;sock&gt; and it renders normally while an asyncio
listener on the same loop lets another process drive it. Handlers touch
the UI directly (same loop, no thread hop), so every injected key has the
identical on-screen effect a keyboard would — the point for livestreaming.

Newline-delimited JSON. v1 methods: keys/text (raw injection, via the same
_press_keys the pilot uses; text can interleave wait:&lt;ms&gt; for a typed-out
look), state/view/screen/functions (introspection; screen() is a full
plain-text render of exactly what the viewer sees). No auth by design —
the socket is 0600, local only.

tests/rpc_smoke.py drives it end-to-end over a real socket (7 green).
</content>
</entry>
<entry>
<title>hex view: file offsets + 'g' goto (dedicated input)</title>
<updated>2026-07-10T12:53:20Z</updated>
<author>
<name>blasty</name>
<email>blasty@local</email>
</author>
<published>2026-07-10T12:53:20Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/ida-tui.git/commit/?id=b8d0335682758ef51eab1c6dea6435b11d4e0bd2'/>
<id>urn:sha1:b8d0335682758ef51eab1c6dea6435b11d4e0bd2</id>
<content type='text'>
Add a file_regions server tool (get_fileregion_offset per segment) and
Program.file_regions/file_offset so a virtual address maps to its raw
on-disk offset without a format-specific header parser. The hex view now
shows a VA column and a file-offset column side by side, and the status
line reports file+off.

Goto moves to its own top-level #goto Input instead of overloading the
function-filter box (which is hidden with the names pane, so goto was
unreachable there). In the hex view 'g' jumps the cursor to an address
(bounds-checked against the image); elsewhere it navigates as before.
</content>
</entry>
<entry>
<title>hex view: raw image bytes (VA-addressed), synced to the code cursor</title>
<updated>2026-07-10T12:03:16Z</updated>
<author>
<name>blasty</name>
<email>blasty@local</email>
</author>
<published>2026-07-10T12:03:16Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/ida-tui.git/commit/?id=636a6cb34e83523c3c513fb62ca39e0f6c10098f'/>
<id>urn:sha1:636a6cb34e83523c3c513fb62ca39e0f6c10098f</id>
<content type='text'>
New HexView: a line-virtualized 16-bytes/row dump of the whole loaded image,
addressed by virtual address. Format-agnostic — the range/segments come from IDA
(image_range over sections()), not any file header; gaps read back as zeros.

Backslash toggles it, synced to the address under the disasm/pseudocode cursor
(see the naked bytes of some code/data). In hex: hjkl/paging navigate a byte
cursor (status shows the section), Enter jumps the code view to the byte,
Tab/Esc/backslash return to the preferred code view. A third _active state; block
-cached + prefetched like the disasm model.

domain: HexModel + Program.hex_model/read_bytes/image_range (get_bytes regions).
Fixed a self-deadlock (hex_model held _lock while sections() re-locked).
Pilot: sync, actual bytes, byte-step, return. full suite 98/98.
</content>
</entry>
<entry>
<title>retype: set variable/function types with 'y' (IDA-style), via structured tools</title>
<updated>2026-07-10T11:30:48Z</updated>
<author>
<name>blasty</name>
<email>blasty@local</email>
</author>
<published>2026-07-10T11:30:48Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/ida-tui.git/commit/?id=6d83015906f850b872261655f014ef14612a8ba5'/>
<id>urn:sha1:6d83015906f850b872261655f014ef14612a8ba5</id>
<content type='text'>
Instead of parsing pseudocode text, add structured server tools (server/
patch_server.py, alongside del_type):
 - func_types(addr): prototype + local variables (name/type/is_arg)
 - set_lvar_type(addr,var,type): retype a decompiler local, working on auto/
   register vars too (stock set_type only updates already-user-modified lvars)

'y' in a code view retypes what's under the cursor: a local variable (prompt
prefilled with its current type) or a function (prompt prefilled with its full
prototype). Applies via set_lvar_type / set_type, then recompiles + refreshes.
Copy-line moved off 'y' to Ctrl+Y.

domain: Program.func_types / set_function_type / set_lvar_type (LVar/FuncTypes).
Pilot: function-prototype retype (prefill + apply). full suite 94/94.
</content>
</entry>
<entry>
<title>struct editor: auto-format the definition on save</title>
<updated>2026-07-10T11:02:05Z</updated>
<author>
<name>blasty</name>
<email>blasty@local</email>
</author>
<published>2026-07-10T11:02:05Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/ida-tui.git/commit/?id=ae3399da08a54e3ecdc8b4c3e622efbf54b55dae'/>
<id>urn:sha1:ae3399da08a54e3ecdc8b4c3e622efbf54b55dae</id>
<content type='text'>
After a successful declare, re-fetch the canonical struct_source and put it back
in the editor, so the normalized layout/types show immediately instead of only
after re-selecting the struct from the list (e.g. a pasted one-liner becomes
multi-line, 'unsigned long' -&gt; 'unsigned __int64'). _loaded_src is updated to the
formatted text so it isn't considered dirty. Pilot check. full suite 92/92.
</content>
</entry>
<entry>
<title>struct editor: loud save errors + guard unsaved edits</title>
<updated>2026-07-10T10:38:51Z</updated>
<author>
<name>blasty</name>
<email>blasty@local</email>
</author>
<published>2026-07-10T10:38:51Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/ida-tui.git/commit/?id=50cbb7e982e03be863ad7f1979638520ffc95f55'/>
<id>urn:sha1:50cbb7e982e03be863ad7f1979638520ffc95f55</id>
<content type='text'>
A struct with a field IDA's C parser rejects (notably '__unused', a predefined
macro) failed to re-declare, but the failure was silent: the edit looked applied,
then reloading showed the old definition (lost work). Now:

- a failed declare shows a loud, styled 'save failed' status and, when it can,
  names the offending reserved field ('__unused') instead of IDA's empty
  'Failed to parse'; the type is left unchanged and the editor keeps your text.
- switching structs / starting new / closing with unsaved edits prompts to
  discard first (ConfirmScreen), so edits aren't silently reloaded away.

Pilot: rejected save is loud/named/non-destructive; switching structs guards
unsaved edits. full suite 91/91.
</content>
</entry>
</feed>
