rust/tty/demo_tty.rs · 14151 字节 · 下载原文
// demo_tty.rs —— **彩色控制台**示范(Rust)
// demo_tty.rs -- the **color console** demo (Rust)
//
// 四层示范的第二层:清屏 + 设前景/背景色 + 定位光标 + 打印彩色文字,正常结束。
// The second of the four demo layers: clear the screen + set foreground/background colors + position the cursor + print colored text, then exit normally.
//
// ◆ 为什么直接发 ANSI,而不是用 `Lib/rust/conio.vml`
// ◆ Why emit ANSI directly instead of using `Lib/rust/conio.vml`
//
// `Lib/rust/` 下确实有一份 `conio.vml`(GenLib 生成的包装器),但**它在这门前端上
// There really is a `conio.vml` under `Lib/rust/` (a GenLib-generated wrapper), but **it is
// 用不了**,三条路都实测过:
// unusable on this frontend**; all three routes were measured:
// · 不写 `#param lib("conio")`:`clrscr` 这类名字不在 `SharedPrefixMap` 里
// · Without `#param lib("conio")`: names like `clrscr` are not in `SharedPrefixMap`
// (那张表决定自动链接)⇒ 报「未定义的函数 'func_clrscr'」;
// (that table decides auto-linking) => it reports "undefined function 'func_clrscr'";
// · 写 `#param lib("conio")`:包**确实**被链了进来(链接日志里有
// · With `#param lib("conio")`: the package **is** linked in (the link log shows
// `成功链接: conio.vml`),但符号对不上 —— 包装器里定义的标签是 `rust_clrscr`
// `linked successfully: conio.vml`), but the symbols do not match -- the labels defined in the wrapper have the shape `rust_clrscr`,
// 这种形状,而用户侧写 `clrscr()` 编成 `func_clrscr`、写 `rust_clrscr()` 编成
// while on the user side `clrscr()` compiles to `func_clrscr` and `rust_clrscr()` compiles to
// `func_rust_clrscr`,两个都解析不到;
// `func_rust_clrscr`; neither one resolves;
// · `--lib <Lib/rust/conio.vml>`:同样无效。
// · `--lib <Lib/rust/conio.vml>`: equally ineffective.
// ⇒ 这一层就直接产出**真终端会产出的那些字节**(清屏 `ESC[2J`、定位 `ESC[{行};{列}H`、
// => This layer simply emits **the bytes a real terminal would emit** (clear screen `ESC[2J`, positioning `ESC[{row};{col}H`,
// 颜色 SGR)。那也是一份"支持彩色的控制台程序",而且在命令行页上与
// color SGR). That is also a "console program with color support", and on the command-line page it goes through
// `Examples/c/conio_screen.c` 走**同一条渲染链**(`AnsiMarkup`)。
// **the same rendering chain** as `Examples/c/conio_screen.c` (`AnsiMarkup`).
//
// ◆ Rust 发 ESC 的三条实测约束(这一份的形状全是被它们逼出来的)
// ◆ Three measured constraints on emitting ESC from Rust (this file's shape is entirely forced by them)
//
// · **`\x1b` 不解析**(原样打出四个字符 `x1b`),`\e` 也不认;
// · **`\x1b` is not parsed** (it prints the four characters `x1b` verbatim), and `\e` is not recognized either;
// · **`\033` 会截断字符串** —— 它被解成 `\0`(NUL)+ 字面量 `33`,字符串在 NUL 处
// · **`\033` truncates the string** -- it is split into `\0` (NUL) + the literal `33`, and the string
// 终止。这与 C 前端那条已知缺陷同形,写 ANSI 的程序全都会踩;
// terminates at the NUL. This has the same shape as that known C-frontend defect, and every program that emits ANSI hits it;
// · **`printx_string` / `print_str_no_nl` 都不可靠** —— 实测 `putchar(27)` +
// · **Both `printx_string` and `print_str_no_nl` are unreliable** -- measured that after `putchar(27)` +
// `printx_string("[2J")` 之后**后面所有输出全部丢失**(连着调 `print_str_no_nl`
// `printx_string("[2J")` **all later output is lost** (calling `print_str_no_nl` back to back
// 还会多写一个 `?`);`print!` 更是直接报「未定义的函数 'rust_print'」。
// writes an extra `?`); `print!` outright reports "undefined function 'rust_print'".
// ⇒ 唯一可靠的组合是 **`putchar(27)` + `println!`**:ESC 单独发一个字节,转义序列的
// => The only reliable combination is **`putchar(27)` + `println!`**: send ESC as its own byte, and write the
// **其余部分**与正文一起写在 `println!` 的字面量里。
// **rest of the escape sequence** together with the text inside the `println!` literal.
// ⚠ **2026-10-04 复验**:`\x1b` 仍原样打 `x1b`(`println!("X\x1bY")` → `Xx1bY`)、`\e` 也仍不认(`P\eQ` → `PeQ`);
// ⚠ **re-checked 2026-10-04**: `\x1b` still prints `x1b` (`println!("X\x1bY")` -> `Xx1bY`) and `\e` is still unrecognized (`P\eQ` -> `PeQ`);
// `\033` 仍截断(`println!("A\033[2JB")` 只打出 `A`)。`putchar(27)` + `printx_string("[2J")` 之后 `println!("AFTER")` 的输出**确实丢失**;
// `\033` still truncates (`println!("A\033[2JB")` prints only `A`). After `putchar(27)` + `printx_string("[2J")`, the output of `println!("AFTER")` **is indeed lost**;
// 而 `print_str_no_nl` **单用是好的**(`putchar(27); print_str_no_nl("[2J"); println!("AFTER")` → `\033[2JAFTER`),跟在 `printx_string` 之后才被一起吃;
// while `print_str_no_nl` **on its own is fine** (`putchar(27); print_str_no_nl("[2J"); println!("AFTER")` -> `\033[2JAFTER`) and is only swallowed after `printx_string`;
// `print!` 不再报「未定义的函数 'rust_print'」,改成打出**地址**(`print!("X")` → `1030`)。
// `print!` no longer reports 「undefined function 'rust_print'」 and instead prints an **address** (`print!("X")` -> `1030`).
// ⇒ 结论不变:可靠组合仍是 `putchar(27)` + `println!`(本文件写法照旧,能跑就别动)。
// => conclusion unchanged: the reliable combination is still `putchar(27)` + `println!` (this file's form stays as is; if it runs, leave it alone).
//
// ◆ 这个形状为什么是对的(一条 ANSI 常识,本仓踩过反面)
// ◆ Why this shape is correct (one piece of ANSI common sense; this repo has hit the opposite case)
//
// `println!` 每行会补一个换行,所以**每行只能发一条 CSI**。要在一行上既定位又上色,
// `println!` appends a newline per line, so **each line can carry only one CSI**. To position and color on one line,
// 直觉写法是 `ESC[6;1H` + `ESC[0;30m` 两条 —— 那需要两次 `println!`。这一份把**多条 SGR
// the intuitive form is the two sequences `ESC[6;1H` + `ESC[0;30m` -- which needs two `println!` calls. This file **merges several SGR
// 合并进同一条 CSI**(`ESC[0;1;44;97m` = 复位 + 粗体 + 蓝底 + 亮白前景),于是"上色"与
// into one CSI** (`ESC[0;1;44;97m` = reset + bold + blue background + bright white foreground), so "coloring" and
// "定位"各占一行:上色那行只发 CSI、不带正文(多出来的换行白走一行,无所谓),
// "positioning" each get their own line: the coloring line sends only the CSI with no text (the extra newline just moves down one line, which is fine),
// 定位那行把正文写在 `ESC[{行};{列}H` 后面。**每行都绝对定位** ⇒ 中间空走的换行不串位。
// and the positioning line writes the text after `ESC[{row};{col}H`. **Every line is absolutely positioned** => the skipped newlines cannot shift anything.
// ⚠ **反面写法**(本仓踩过):`ESC[6;1H[0m[30m` —— 只有第一条 `ESC[6;1H` 是转义序列,
// ⚠ **The wrong form** (this repo has hit it): `ESC[6;1H[0m[30m` -- only the first `ESC[6;1H` is an escape sequence,
// 后面 `[0m[30m` 会被终端当成**正文**打出来。多段转义必须各带自己的 ESC。
// the following `[0m[30m` is printed by the terminal as **text**. Each escape segment must carry its own ESC.
//
// 跑法:命令行页输入 vml run examples/rust/demo_tty.rs
// How to run: type this into the command-line page: vml run examples/rust/demo_tty.rs
// ◆ UI 库的显式引用
// The explicit reference to the UI library
//
// `use vml_ui::*;` —— 声明文件是 `Lib/rust/vml_ui.rs`(GenLib 生成,155 个 `ui_*`,**不要手改**):
// `use vml_ui::*;` -- the declaration file is `Lib/rust/vml_ui.rs` (generated by GenLib, 155 `ui_*`; **do not hand-edit**):
//
// cd third_party/vml && dotnet run --project tools/GenLib -- -n -c vmlui -l rust -o Lib/rust/vml_ui.rs
//
// 本前端**不需要**声明就能解析 `ui_*`(它按 `ui_` 前缀自动链 `vmlui.vml`),
// This frontend resolves `ui_*` **without** any declaration (it auto-links `vmlui.vml` by the `ui_` prefix),
// 这条引用是**文档级**的:它标明"这个程序用 UI 库",并让这份例程与其余语言同形。
// so this line is **documentation-level**: it states that this program uses the UI library and keeps
// this example shaped like every other language's.
// 实测(删掉这一行,产物与画面完全一致):见仓库 docs 里那份「UI 库导入约定」的第二节 rust 行。
// Measured: deleting this line changes nothing -- the same instruction count and the same drawn picture;
// see the rust row in section two of the UI-library import convention document under docs.
#include "../../../Lib/rust/vml_ui.rs"
use vml_ui::*;
fn main() {
let lang = ui_scr_get_language();
putchar(27);
println!("[2J");
putchar(27);
println!("[H");
putchar(27);
println!("[0;1;44;97m");
putchar(27);
if lang == 0 { println!("[3;1H === Dolaima 彩色控制台演示 (Rust) ==="); } else { println!("[3;1H === Dolaima color console demo (Rust) ==="); }
putchar(27);
println!("[0;37m");
putchar(27);
if lang == 0 { println!("[5;1H标准 8 色前景(30-37):"); } else { println!("[5;1HStandard 8 foreground colors (30-37):"); }
putchar(27);
println!("[0;30m");
putchar(27);
if lang == 0 { println!("[6;1H 30 黑"); } else { println!("[6;1H 30 black"); }
putchar(27);
println!("[0;31m");
putchar(27);
if lang == 0 { println!("[7;1H 31 红"); } else { println!("[7;1H 31 red"); }
putchar(27);
println!("[0;32m");
putchar(27);
if lang == 0 { println!("[8;1H 32 绿"); } else { println!("[8;1H 32 green"); }
putchar(27);
println!("[0;33m");
putchar(27);
if lang == 0 { println!("[9;1H 33 黄"); } else { println!("[9;1H 33 yellow"); }
putchar(27);
println!("[0;34m");
putchar(27);
if lang == 0 { println!("[10;1H 34 蓝"); } else { println!("[10;1H 34 blue"); }
putchar(27);
println!("[0;35m");
putchar(27);
if lang == 0 { println!("[11;1H 35 品红"); } else { println!("[11;1H 35 magenta"); }
putchar(27);
println!("[0;36m");
putchar(27);
if lang == 0 { println!("[12;1H 36 青"); } else { println!("[12;1H 36 cyan"); }
putchar(27);
println!("[0;37m");
putchar(27);
if lang == 0 { println!("[13;1H 37 白"); } else { println!("[13;1H 37 white"); }
putchar(27);
println!("[0;37m");
putchar(27);
if lang == 0 { println!("[15;1H亮色前景(90-97)与背景色:"); } else { println!("[15;1HBright foreground (90-97) and background colors:"); }
putchar(27);
println!("[0;91m");
putchar(27);
if lang == 0 { println!("[16;1H 91 亮红"); } else { println!("[16;1H 91 bright red"); }
putchar(27);
println!("[0;92m");
putchar(27);
if lang == 0 { println!("[17;1H 92 亮绿"); } else { println!("[17;1H 92 bright green"); }
putchar(27);
println!("[0;41m");
putchar(27);
if lang == 0 { println!("[18;1H 41 红底"); } else { println!("[18;1H 41 red bg"); }
putchar(27);
println!("[0;104m");
putchar(27);
if lang == 0 { println!("[19;1H 104 亮蓝底"); } else { println!("[19;1H 104 bright blue bg"); }
putchar(27);
println!("[0;103m");
putchar(27);
if lang == 0 { println!("[20;1H 103 亮黄底"); } else { println!("[20;1H 103 bright yellow bg"); }
putchar(27);
println!("[0;37m");
putchar(27);
if lang == 0 { println!("[22;1H256 色(38;5;N)与真彩(38;2;r;g;b):"); } else { println!("[22;1H256-color (38;5;N) and truecolor (38;2;r;g;b):"); }
putchar(27);
println!("[0;38;5;208m");
putchar(27);
if lang == 0 { println!("[23;1H 256-208 橙"); } else { println!("[23;1H 256-208 orange"); }
putchar(27);
println!("[0;38;5;46m");
putchar(27);
if lang == 0 { println!("[24;1H 256-46 亮绿"); } else { println!("[24;1H 256-46 bright green"); }
putchar(27);
println!("[0;38;2;255;128;0m");
putchar(27);
if lang == 0 { println!("[25;1H 真彩 橙"); } else { println!("[25;1H truecolor orange"); }
putchar(27);
println!("[0;38;2;0;200;255m");
putchar(27);
if lang == 0 { println!("[26;1H 真彩 青"); } else { println!("[26;1H truecolor cyan"); }
putchar(27);
println!("[0;36m");
putchar(27);
if lang == 0 { println!("[28;1H光标定位(把光标移到第 30 行第 5 列)+ 画框:"); } else { println!("[28;1HCursor positioning (move the cursor to row 30, column 5) + draw a box:"); }
putchar(27);
println!("[30;5H+----------------+");
putchar(27);
if lang == 0 { println!("[31;5H| 定位画框 |"); } else { println!("[31;5H| located box |"); }
putchar(27);
println!("[32;5H+----------------+");
putchar(27);
println!("[0;2m");
putchar(27);
if lang == 0 { println!("[34;1H(以上全部是 ANSI SGR 序列,由终端 / 命令行页的 AnsiMarkup 渲染)"); } else { println!("[34;1H(all of the above are ANSI SGR sequences, rendered by the terminal / AnsiMarkup on the command-line page)"); }
putchar(27);
println!("[0;32m");
putchar(27);
if lang == 0 { println!("[36;1H结束 —— 正常退出"); } else { println!("[36;1Hdone -- exiting normally"); }
}