lua/tty/demo_tty.lua · 15533 字节 · 下载原文
-- demo_tty.lua —— **第 2 层:彩色控制台**
-- demo_tty.lua — **Layer 2: color console**
--
-- 这一层解决的问题是「**在一个字符界面上做版面**」:清屏、把光标挪到第几行第几列、
-- The problem this layer solves is "**laying out a character-cell screen**": clearing the screen, moving the cursor to a given row and column,
-- 设前景/背景色。老 DOS 程序(菜单、表格、状态栏)全靠这几个原语拼出来。
-- setting foreground/background colors. Old DOS programs (menus, tables, status bars) were all assembled from these few primitives.
--
-- Lua 这一层**没有 conio/crt 绑定**,所以这里**直接输出 ANSI 转义序列**
-- Lua has **no conio/crt bindings** for this layer, so here we **emit ANSI escape sequences directly**
-- —— 那正是"支持彩色的控制台程序":
-- — which is exactly what "a color-capable console program" is:
--
-- 清屏 ESC [ 2 J ESC [ H
-- clear screen ESC [ 2 J ESC [ H
-- 定位光标 ESC [ <行> ; <列> H
-- cursor to ESC [ <row> ; <col> H
-- 前景/背景 ESC [ 3x m / 4x m / 9x m / 10x m,256 色 38;5;N,真彩 38;2;r;g;b
-- fg/bg ESC [ 3x m / 4x m / 9x m / 10x m, 256-color 38;5;N, truecolor 38;2;r;g;b
-- 复位 ESC [ 0 m
-- reset ESC [ 0 m
--
-- ## ⚠ 本前端的两条限制(写 demo 时避开)
-- ## ⚠ Two limits of this frontend (avoid them when writing demos)
--
-- · ~~**`..` 字符串拼接编不过** —— 链到 `lua_concat`,库里没有这个标签~~
-- · ~~**`..` string concatenation does not compile** — it links to `lua_concat`, and the library has no such label~~
-- ✅ **已修好(2026-10-04 复验)**:`local s = "AB" .. "CD"` 再 `print_str(s)` ⇒ `ABCD`
-- ✅ **Fixed (re-verified 2026-10-04)**: `local s = "AB" .. "CD"` then `print_str(s)` => `ABCD`
-- (⚠ 用 `print` 打非字面量字符串打的不是那个串)。本文件仍把转义串整条写字面量(写法无害)。
-- (⚠ using `print` on a non-literal string does not print that string). This file still writes escape strings as whole literals (harmless).
-- · **`putchar(n)` 中间夹了别的库调用就读错实参** —— 单条连着调时是对的
-- · **`putchar(n)` reads the wrong argument when another library call is wedged in between** — a straight run of calls is correct
-- (`putchar(65) putchar(66) putchar(27)` → `A B ESC`),而
-- (`putchar(65) putchar(66) putchar(27)` → `A B ESC`), while
-- `putchar(65)` `print_str("\n")` `putchar(27)` 里的第三个字节实测是 `A7 AD`
-- in `putchar(65)` `print_str("\n")` `putchar(27)` the third byte measures as `A7 AD`
-- 而不是 `1B`(与 `Lib/` 那两套栈清理约定同源,见 CLAUDE.md ⑨)。
-- instead of `1B` (same root cause as the two stack-cleanup conventions in `Lib/`, see CLAUDE.md ⑨).
-- 好在 Lua 的字符串字面量**支持 `\x1b`**(走 `LexerBase.ReadEscape`),
-- Fortunately Lua string literals **support `\x1b`** (via `LexerBase.ReadEscape`),
-- 所以内联 ESC 就好、用不着 putchar —— 本份一个字都没用 putchar。
-- so inlining ESC is enough and putchar is not needed — this file does not use putchar even once.
-- · `print` 会补换行 ⇒ 这里一律用**不补换行**的 `print_str`。
-- · `print` appends a newline ⇒ here we always use `print_str`, which **appends no newline**.
--
-- ## 在哪儿能看到什么
-- ## Where you can see what
--
-- · **真终端**(`vmlcli … | cat -v`):整套转义都生效。
-- · **A real terminal** (`vmlcli … | cat -v`): the whole escape set takes effect.
-- · **手机"命令行"页**:那一层把 stdout 里的 ANSI 转成仓库统一的 `«»` 中间格式
-- · **The phone's "command line" page**: that layer converts ANSI on stdout into this repo's unified `«»` intermediate format
-- (`UI/Shared/AnsiMarkup.cs`),**只认 SGR(颜色/样式)**;光标定位(`ESC[…H`)
-- (`UI/Shared/AnsiMarkup.cs`), and **only recognizes SGR (color/style)**; cursor positioning (`ESC[…H`)
-- 与清屏(`ESC[2J`)会被**吃掉** —— 这是刻意的有限子集(见
-- and screen clearing (`ESC[2J`) are **swallowed** — this is a deliberately limited subset (see
-- `Examples/c/ansi_colors.c` 的说明)。所以每行末尾都补了换行,
-- the notes in `Examples/c/ansi_colors.c`). So a newline is appended at the end of every line, and
-- 定位被吃掉时输出仍然一行一句、读得通。
-- when positioning is swallowed the output is still one sentence per line and readable.
--
-- 跑法:命令行页输入 vml run examples/lua/demo_tty.lua
-- How to run: on the command-line page type vml run examples/lua/demo_tty.lua
-- ── 开场:清屏 + 回左上角 ───────────────────────────────────────
-- ── Opening: clear the screen + go back to the top-left corner ───────────────────────────────────────
-- ── 显式引用 UI 库的声明文件 ────────────────────────────────────────────────
-- Explicit reference to the UI library's declaration file.
--
-- `Lib/lua/vml_ui.lua` 由 `GenLib -n -c vmlui -l lua` 从 `Lib/shared/src/vmlui.c` 生成(155 个 `ui_*`)。
-- `Lib/lua/vml_ui.lua` is produced by `GenLib -n -c vmlui -l lua` from `Lib/shared/src/vmlui.c` (155 `ui_*`).
-- **不要手写、也不要手改生成物**(要改就改生成器 `tools/GenLib`)。
-- Never hand-write it and never edit the generated file (change the generator `tools/GenLib` instead).
--
-- ⚠ 这条 `require` 对 Lua 是**文档级**引用:本前端按名字解析库函数,删掉它照样编得过、跑得对。
-- ⚠ For Lua this `require` is a **documentation-level** reference: the front end resolves library
-- functions by name, so removing it still compiles and runs.
-- 实测结论与命令见 `docs/UI库导入约定.md` 的「lua 实测结论」一节。
-- Measured results and the exact commands are in the UI-library import convention doc (Lua section).
require("vml_ui")
local lang = ui_scr_get_language()
print_str("\x1b[2J")
print_str("\x1b[H")
-- ── 标题条:蓝底白字(ESC[44;97m)──────────────────────────────
-- ── Title bar: white on blue (ESC[44;97m) ──────────────────────────────
print_str("\x1b[1;1H")
print_str("\x1b[44;97m")
if lang == 0 then print_str(" demo_tty (Lua) —— 彩色控制台 / ANSI 转义序列 ")
else print_str(" demo_tty (Lua) -- color console / ANSI escape sequences ") end
print_str("\x1b[0m")
print_str("\n")
print_str("\x1b[2;1H")
print_str("\x1b[90m")
if lang == 0 then print_str("清屏 ESC[2J 定位 ESC[r;cH 颜色 ESC[3xm / ESC[4xm 复位 ESC[0m")
else print_str("clear ESC[2J cursor ESC[r;cH color ESC[3xm / ESC[4xm reset ESC[0m") end
print_str("\x1b[0m")
print_str("\n")
-- ── 标准 8 色前景(30–37)──────────────────────────────────────
-- ── Standard 8 foreground colors (30–37) ──────────────────────────────────────
print_str("\x1b[4;1H")
print_str("\x1b[1;37m")
if lang == 0 then print_str("标准 8 色前景:")
else print_str("Standard 8 foreground colors:") end
print_str("\x1b[0m")
print_str("\n")
print_str("\x1b[5;1H")
print_str("\x1b[30m")
if lang == 0 then print_str(" 30 黑 ")
else print_str(" 30 black ") end
print_str("\x1b[0m")
print_str("\x1b[31m")
if lang == 0 then print_str(" 31 红 ")
else print_str(" 31 red ") end
print_str("\x1b[0m")
print_str("\x1b[32m")
if lang == 0 then print_str(" 32 绿 ")
else print_str(" 32 green ") end
print_str("\x1b[0m")
print_str("\x1b[33m")
if lang == 0 then print_str(" 33 黄 ")
else print_str(" 33 yellow ") end
print_str("\x1b[0m")
print_str("\x1b[34m")
if lang == 0 then print_str(" 34 蓝 ")
else print_str(" 34 blue ") end
print_str("\x1b[0m")
print_str("\x1b[35m")
if lang == 0 then print_str(" 35 品红 ")
else print_str(" 35 magenta ") end
print_str("\x1b[0m")
print_str("\x1b[36m")
if lang == 0 then print_str(" 36 青 ")
else print_str(" 36 cyan ") end
print_str("\x1b[0m")
print_str("\x1b[37m")
if lang == 0 then print_str(" 37 白 ")
else print_str(" 37 white ") end
print_str("\x1b[0m")
print_str("\n")
-- ── 亮色前景(90–97)────────────────────────────────────────────
-- ── Bright foreground colors (90–97) ────────────────────────────────────────────
print_str("\x1b[6;1H")
print_str("\x1b[90m")
if lang == 0 then print_str(" 90 亮黑(灰) ")
else print_str(" 90 bright black ") end
print_str("\x1b[0m")
print_str("\x1b[91m")
if lang == 0 then print_str(" 91 亮红 ")
else print_str(" 91 bright red ") end
print_str("\x1b[0m")
print_str("\x1b[92m")
if lang == 0 then print_str(" 92 亮绿 ")
else print_str(" 92 bright green ") end
print_str("\x1b[0m")
print_str("\x1b[93m")
if lang == 0 then print_str(" 93 亮黄 ")
else print_str(" 93 bright yellow ") end
print_str("\x1b[0m")
print_str("\x1b[94m")
if lang == 0 then print_str(" 94 亮蓝 ")
else print_str(" 94 bright blue ") end
print_str("\x1b[0m")
print_str("\x1b[95m")
if lang == 0 then print_str(" 95 亮品红 ")
else print_str(" 95 bright magenta ") end
print_str("\x1b[0m")
print_str("\x1b[96m")
if lang == 0 then print_str(" 96 亮青 ")
else print_str(" 96 bright cyan ") end
print_str("\x1b[0m")
print_str("\x1b[97m")
if lang == 0 then print_str(" 97 亮白 ")
else print_str(" 97 bright white ") end
print_str("\x1b[0m")
print_str("\n")
-- ── 背景色(40–47 / 100–107)───────────────────────────────────
-- ── Background colors (40–47 / 100–107) ───────────────────────────────────
print_str("\x1b[8;1H")
print_str("\x1b[1;37m")
if lang == 0 then print_str("背景色:")
else print_str("Background colors:") end
print_str("\x1b[0m")
print_str("\n")
print_str("\x1b[9;1H")
print_str("\x1b[41m")
if lang == 0 then print_str(" 红底 ")
else print_str(" red bg ") end
print_str("\x1b[0m")
print_str("\x1b[42m")
if lang == 0 then print_str(" 绿底 ")
else print_str(" green bg ") end
print_str("\x1b[0m")
print_str("\x1b[44m")
if lang == 0 then print_str(" 蓝底 ")
else print_str(" blue bg ") end
print_str("\x1b[0m")
print_str("\x1b[46m")
if lang == 0 then print_str(" 青底 ")
else print_str(" cyan bg ") end
print_str("\x1b[0m")
print_str("\x1b[103m")
if lang == 0 then print_str(" 亮黄底 ")
else print_str(" bright yellow bg ") end
print_str("\x1b[0m")
print_str("\x1b[105m")
if lang == 0 then print_str(" 亮品红底 ")
else print_str(" bright magenta bg ") end
print_str("\x1b[0m")
print_str("\n")
-- ── 样式(1 粗 / 2 暗 / 3 斜 / 4 下划线 / 9 删除线)─────────────
-- ── Styles (1 bold / 2 dim / 3 italic / 4 underline / 9 strikethrough) ─────────────
print_str("\x1b[11;1H")
print_str("\x1b[1;37m")
if lang == 0 then print_str("样式:")
else print_str("Styles:") end
print_str("\x1b[0m")
print_str("\x1b[1m")
if lang == 0 then print_str(" 粗体 ")
else print_str(" bold ") end
print_str("\x1b[0m")
print_str("\x1b[2m")
if lang == 0 then print_str(" 暗淡 ")
else print_str(" dim ") end
print_str("\x1b[0m")
print_str("\x1b[3m")
if lang == 0 then print_str(" 斜体 ")
else print_str(" italic ") end
print_str("\x1b[0m")
print_str("\x1b[4m")
if lang == 0 then print_str(" 下划线 ")
else print_str(" underline ") end
print_str("\x1b[0m")
print_str("\x1b[9m")
if lang == 0 then print_str(" 删除线 ")
else print_str(" strikethrough ") end
print_str("\x1b[0m")
print_str("\n")
-- ── 256 色(38;5;N)与真彩(38;2;r;g;b)─────────────────────────
-- ── 256 colors (38;5;N) and truecolor (38;2;r;g;b) ─────────────────────────
print_str("\x1b[13;1H")
print_str("\x1b[1;37m")
if lang == 0 then print_str("256 色 / 真彩:")
else print_str("256-color / truecolor:") end
print_str("\x1b[0m")
print_str("\x1b[38;5;208m")
if lang == 0 then print_str(" 256-208 橙 ")
else print_str(" 256-208 orange ") end
print_str("\x1b[0m")
print_str("\x1b[38;5;46m")
if lang == 0 then print_str(" 256-46 亮绿 ")
else print_str(" 256-46 bright green ") end
print_str("\x1b[0m")
print_str("\x1b[38;2;255;128;0m")
if lang == 0 then print_str(" 真彩橙 ")
else print_str(" truecolor orange ") end
print_str("\x1b[0m")
print_str("\x1b[48;2;60;0;90m")
if lang == 0 then print_str(" 真彩深紫底 ")
else print_str(" truecolor dark purple bg ") end
print_str("\x1b[0m")
print_str("\n")
-- ── 循环画一条色带(12 格,颜色在 8 档里循环)──────────────────
-- ── Draw a color band in a loop (12 cells, colors cycling through 8 steps) ──────────────────
-- 「算出来的」那一格:取模 + 分支挑字面量色码(`..` 不能用,见文件头)。
-- The "computed" cell: modulo + a branch picking a literal color code (`..` cannot be used, see the file header).
print_str("\x1b[16;1H")
print_str("\x1b[1;37m")
if lang == 0 then print_str("循环画 12 格色带:")
else print_str("Draw a 12-cell color band in a loop:") end
print_str("\x1b[0m")
print_str("\n")
print_str("\x1b[17;1H")
local i = 0
while i < 12 do
local k = i % 8
if k == 0 then
print_str("\x1b[41m")
end
if k == 1 then
print_str("\x1b[42m")
end
if k == 2 then
print_str("\x1b[43m")
end
if k == 3 then
print_str("\x1b[44m")
end
if k == 4 then
print_str("\x1b[45m")
end
if k == 5 then
print_str("\x1b[46m")
end
if k == 6 then
print_str("\x1b[47m")
end
if k == 7 then
print_str("\x1b[100m")
end
print_str(" ")
print_str("\x1b[0m")
i = i + 1
end
print_str("\n")
-- ── 收尾:复位颜色 + 定位到第 20 行写结束语 ─────────────────────
-- ── Wrap-up: reset the colors + position to row 20 and write the closing line ─────────────────────
print_str("\x1b[0m")
print_str("\x1b[20;1H")
print_str("\x1b[1;32m")
if lang == 0 then print_str("demo_tty 结束 —— 没有按键等待,画完即退出。")
else print_str("demo_tty done -- no key wait, draws and exits.") end
print_str("\x1b[0m")
print_str("\n")