aboutsummaryrefslogtreecommitdiffstats
path: root/experiments
diff options
context:
space:
mode:
authorblasty <peter@haxx.in>2026-08-21 12:08:42 +0200
committerblasty <peter@haxx.in>2026-08-21 12:08:42 +0200
commit7a3b198a904da186cc2860df059b063db24282ea (patch)
treefd5a754169522c0ccd8b7e0d70b2238395d81b09 /experiments
parentdocs: be honest that the triskel fork is unpublished (diff)
downloadida-tui-7a3b198a904da186cc2860df059b063db24282ea.tar.gz
ida-tui-7a3b198a904da186cc2860df059b063db24282ea.tar.xz
ida-tui-7a3b198a904da186cc2860df059b063db24282ea.zip
splash: give the logo a placement id so re-anchoring replaces, not stacks
A kitty placement is identified by (image id, placement id); an a=p with no p= key is anonymous and every one stacks another copy. The splash re-anchors on every progress note (~5/s), so a 60s load ended with ~300 placements of an RGBA logo alpha-compositing over each other -- soft edges creeping to solid, and the terminal re-rendering all of them per frame. Same pair every time (LOGO_PLACEMENT) = the terminal REPLACES the placement, so re-anchoring is free and atomic. That is also why on_resize no longer clear()s first (that showed a hole for a frame), and why a shrunk-to-nothing region now drops the placement instead of leaving a stale one anchored. tests/test_kittygfx.py pins the escapes (pure, stdlib); experiments/splash_place_count.py counts what a real splash sends.
Diffstat (limited to 'experiments')
-rw-r--r--experiments/splash_place_count.py97
1 files changed, 97 insertions, 0 deletions
diff --git a/experiments/splash_place_count.py b/experiments/splash_place_count.py
new file mode 100644
index 0000000..6d8322a
--- /dev/null
+++ b/experiments/splash_place_count.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+"""Count what the splash actually SENDS to the terminal.
+
+Pushes a real LoadingScreen onto a bare Textual app with kitty graphics forced
+on and kittygfx._write captured, then drives it the way the app does (a status
+write per progress tick) and tallies the escapes.
+
+ PYTHONPATH=. ~/ida-venv/bin/python experiments/splash_place_count.py
+"""
+from __future__ import annotations
+
+import asyncio
+import os
+import re
+import sys
+
+os.environ["IDATUI_KITTY"] = "1"
+
+from textual.app import App, ComposeResult # noqa: E402
+from textual.widgets import Static # noqa: E402
+
+from idatui import kittygfx # noqa: E402
+
+SENT: list[str] = []
+
+
+def _fake_write(data: str) -> bool:
+ SENT.append(data)
+ return True
+
+
+kittygfx._write = _fake_write # type: ignore[assignment]
+kittygfx._cell = (9, 22)
+
+from idatui.app import LoadingScreen # noqa: E402
+
+
+def tally() -> dict[str, int]:
+ blob = "".join(SENT)
+ return {
+ "uploads (a=t)": len(re.findall(r"\x1b_G[^;]*a=t", blob)),
+ "placements (a=p)": len(re.findall(r"\x1b_G[^;]*a=p", blob)),
+ "deletes (a=d)": len(re.findall(r"\x1b_G[^;]*a=d", blob)),
+ "bytes": len(blob),
+ }
+
+
+class Host(App):
+ CSS = "#loading-box { width: 70; height: auto; }"
+
+ def compose(self) -> ComposeResult:
+ yield Static("host")
+
+
+async def main() -> None:
+ ticks = int(sys.argv[1]) if len(sys.argv) > 1 else 40
+ app = Host()
+ async with app.run_test(size=(100, 45)) as pilot:
+ screen = LoadingScreen("target")
+ app.push_screen(screen)
+ await pilot.pause()
+ await pilot.pause()
+ print(f"image mode: {screen._image}")
+ after_mount = tally()
+ print("after mount:", after_mount)
+
+ # what the app does: _status() -> loading_screen.update_note(), once
+ # per progress write. Spread over ~4s of wall clock like a real load.
+ for i in range(ticks):
+ screen.update_note(f"analyzing… {i}")
+ await asyncio.sleep(0.1)
+ await pilot.pause()
+ print(f"after {ticks} progress notes:", tally())
+
+ screen.dismiss()
+ await pilot.pause()
+ print("after dismiss:", tally())
+
+ d = tally()
+ blob = "".join(SENT)
+ cmds = re.findall(r"\x1b_G([^;\x1b]*)", blob)
+ ids = {dict(kv.split("=", 1) for kv in c.split(",") if "=" in kv).get("p")
+ for c in cmds if "a=p" in c.split(",")}
+ onscreen = "unbounded (anonymous)" if None in ids else len(ids)
+ print()
+ print(f"=> {d['placements (a=p)']} place escapes sent, "
+ f"{d['deletes (a=d)']} deletes")
+ print(f" images actually on screen: {onscreen}")
+ print(" A placement is identified by (image id, placement id). An a=p with")
+ print(" no p= key is ANONYMOUS and stacks a fresh copy every time; with a")
+ print(" p= key the terminal replaces the previous one. logo.png is RGBA, so")
+ print(" stacking also composites its soft edges towards solid.")
+ m = re.search(r"\x1b_G(a=p[^;\x1b]*)", blob)
+ print(" placement escape:", m.group(1) if m else "(none)")
+
+
+asyncio.run(main())