← 都来码 / Dolaima← 例程索引fortran/tty/demo_tty.f90原文

fortran/tty/demo_tty.f90 · 13163 字节 · 下载原文

! demo_tty.f90 —— Fortran 第二层:**彩色控制台**(tty)

! demo_tty.f90 -- Fortran layer two: **color console** (tty)

!

! Fortran **没有** conio / crt 那一类可用的控制台绑定:

! Fortran has **no** usable console binding of the conio / crt kind:

!   · `Lib/fortran/crt.vml` 里只有自动生成的空壳

!   · `Lib/fortran/crt.vml` holds only an auto-generated empty shell

!   · `Lib/fortran/console.f90` 的三个函数全是 `r = 0` 的空实现(没有实现体)

!   · all three functions in `Lib/fortran/console.f90` are empty `r = 0` stubs (no body at all)

! 所以这一层直接输出 **ANSI 转义序列** —— 与 `Examples/c/ansi_colors.c` 同一个做法、

! So this layer emits **ANSI escape sequences** directly -- the same approach as `Examples/c/ansi_colors.c`,

! 同一套效果(手机端「命令行」页由 `UI/Shared/AnsiMarkup.cs` 翻成彩色富文本)。

! with the same effect (on mobile the "command line" page is turned into colored rich text by `UI/Shared/AnsiMarkup.cs`).

!

! 跑法(桌面 vmlcli):

! How to run (desktop vmlcli):

!   dotnet scripts/vmlcli/bin/Release/net10.0/vmlcli.dll Examples/fortran/demo_tty.f90

!

! 画面(从上到下):

! What appears on screen (top to bottom):

!   ① 清屏(ESC[2J + ESC[H)+ 复位属性

!   ① Clear the screen (ESC[2J + ESC[H) + reset attributes

!   ② 8 个暗色前景(色号 0-7),背景 = 7 浅灰

!   ② 8 dark foreground colors (color numbers 0-7), background = 7 light gray

!   ③ 8 个亮色前景(色号 8-15),背景 = 0 黑

!   ③ 8 bright foreground colors (color numbers 8-15), background = 0 black

!   ④ 光标定位:同一行先写右半段、再回到左端写左半段 —— 证明是**定位**不是顺序输出

!   ④ Cursor positioning: on the same row write the right half first, then go back to the left end and write the left half -- proving it is **positioning**, not sequential output

!   ⑤ 复位成白字黑底,正常结束

!   ⑤ Reset to white on black, then exit normally

!

! ═══════════════════════════════════════════════════════════════════════

!  ⚠ 这份文件为什么写成"逐字节 putchar"的样子(三条本前端的硬限制)

!  ⚠ Why this file is written as "byte-by-byte putchar" (three hard limitations of this frontend)

!

!  ① **`print *, char(27)` 发不出 ESC**。本前端的 `char()` 是**恒等映射**

!  ① **`print *, char(27)` cannot emit ESC**. In this frontend `char()` is the **identity mapping**

!     (整数转字符 = 无操作),`print` 于是把它当整数打出来(打出 `27` 两个字符)。

!     (integer-to-character is a no-op), so `print` prints it as an integer (the two characters `27`).

!     实测:`call putchar(27)` 真的发出 0x1B ✅ / `print *, char(27)` 打出 "27" ❌

!     Measured: `call putchar(27)` really emits 0x1B OK / `print *, char(27)` prints "27" (wrong)

!     ⇒ 发 ESC 只能走 `call putchar(27)`。

!     => To emit ESC there is no other route than `call putchar(27)`.

!

!  ② **没有可用的子程序**。`contains` 里的内部子程序**传不进实参**、也**看不见

!  ② **No usable subroutines**. A contained subroutine **cannot receive arguments** and **cannot see

!     宿主程序的变量**(实测:`call show(97)` 里 `v` 读到 0;读宿主的全局量同样读到 0;

!     the host program's variables** (measured: inside `call show(97)` `v` reads 0; reading the host's globals also gives 0;

!     外部子程序则直接「未定义的函数 'show'」)。所以本文件**一个子程序都没有** ——

!     an external subroutine just reports "undefined function 'show'"). So this file has **not a single subroutine** --

!     ANSI 序列全部内联展开,这就是它比别的语言那份长一截的原因。

!     every ANSI sequence is expanded inline, which is why it is a good deal longer than the other languages' versions.

!

!  ③ **没有整数转串**。`Str()` 没有、字符串 `//` 拼接没有(`'A' // 'B'` 报

!  ③ **No integer-to-string conversion**. There is no `Str()`, no string `//` concatenation (`'A' // 'B'` reports

!     「意外的 token: Div」)、`character :: s` 变量存不住字符串(`s = 'AB'` 读回来是 0)

!     "unexpected token: Div"), and a `character :: s` variable cannot hold a string (`s = 'AB'` reads back as 0)

!     ⇒ 序列里要发的十进制数字只能自己按位算:`48 + 数字` 就是它的 ASCII 码。

!     => the decimal digits the sequences need must be computed digit by digit: `48 + digit` is their ASCII code.

!

!  本文件里每个 `putchar` 都带行尾注释说明它发的是哪个字符,方便逐字节核对。

!  Every `putchar` in this file carries a trailing comment saying which character it emits, making byte-by-byte checking easy.

! ═══════════════════════════════════════════════════════════════════════


program demo_tty
  implicit none
  integer :: c
  integer :: row, fg, col
  integer :: lang

  ! 界面语言:0 = 中文 / 1 = 英文(ui_scr_get_language 是 syscall,开局查一次)

  ! UI language: 0 = Chinese / 1 = English (ui_scr_get_language is a syscall, queried once at startup)

  ! ⚠ 本前端没有三元/merge,界面文字只能靠单行 `if` 二选一(同 sokoban.f90)。

  ! ⚠ This frontend has no ternary/merge, so UI text can only pick between two options with a single-line `if` (same as sokoban.f90).

  lang = ui_scr_get_language()

  ! ── ① 清屏 + 复位属性 ────────────────────────────────────────────

  ! -- ① Clear the screen + reset attributes --

  call putchar(27)          ! ESC

  call putchar(91)          ! [

  call putchar(48)          ! 0

  call putchar(109)         ! m      ⇒ ESC[0m  复位全部属性

  ! m      => ESC[0m  reset all attributes

  call putchar(27)          ! ESC

  call putchar(91)          ! [

  call putchar(50)          ! 2

  call putchar(74)          ! J      ⇒ ESC[2J  清屏

  ! J      => ESC[2J  clear the screen

  call putchar(27)          ! ESC

  call putchar(91)          ! [

  call putchar(72)          ! H      ⇒ ESC[H   光标回左上

  ! H      => ESC[H   cursor back to the top left


  ! ── 标题:亮白字(97) + 蓝底(44) ─────────────────────────────────

  ! -- Title: bright white text (97) + blue background (44) --

  call putchar(27)          ! ESC

  call putchar(91)          ! [

  call putchar(57)          ! 9

  call putchar(55)          ! 7      ⇒ 「97」亮白前景

  ! 7      => "97" bright white foreground

  call putchar(59)          ! ;

  call putchar(52)          ! 4

  call putchar(52)          ! 4      ⇒ 「44」蓝背景

  ! 4      => "44" blue background

  call putchar(109)         ! m

  print *, '=== Fortran color console demo ===  (ANSI 97;44 = bright white on blue)'

  ! ── ② 暗色 0-7,背景 7 浅灰(SGR 30-37 前景 / 47 背景)──────────

  ! -- ② Dark colors 0-7, background 7 light gray (SGR 30-37 foreground / 47 background) --

  do c = 0, 7
    row = 3 + c
    fg = 30 + c
    ! ESC[<row>;3H —— row 是 3..10,一位或两位

    ! ESC[<row>;3H -- row is 3..10, one or two digits

    call putchar(27)        ! ESC

    call putchar(91)        ! [

    if (row >= 10) then
      call putchar(48 + row / 10)          ! 行号的十位

      ! tens digit of the row number

    end if
    call putchar(48 + row - (row / 10) * 10)   ! 行号的个位

    ! ones digit of the row number

    call putchar(59)        ! ;

    call putchar(51)        ! 3      ⇒ 列固定第 3 列

    ! 3      => the column is always column 3

    call putchar(72)        ! H

    ! ESC[<fg>;47m

    call putchar(27)        ! ESC

    call putchar(91)        ! [

    call putchar(48 + fg / 10)              ! 色号十位(30+c 恒为两位)

    ! tens digit of the color number (30+c is always two digits)

    call putchar(48 + fg - (fg / 10) * 10)  ! 色号个位

    ! ones digit of the color number

    call putchar(59)        ! ;

    call putchar(52)        ! 4

    call putchar(55)        ! 7      ⇒ 「47」浅灰背景

    ! 7      => "47" light gray background

    call putchar(109)       ! m

    if (lang == 0) print *, '前景色', c, ' 暗色,背景 = 7 浅灰'
    if (lang /= 0) print *, 'foreground', c, ' dark, background = 7 light gray'
  end do

  ! ── ③ 亮色 8-15,背景 0 黑(SGR 90-97 前景 / 40 背景)──────────

  ! -- ③ Bright colors 8-15, background 0 black (SGR 90-97 foreground / 40 background) --

  do c = 8, 15
    row = 4 + c
    fg = 90 + (c - 8)
    call putchar(27)        ! ESC

    call putchar(91)        ! [

    call putchar(48 + row / 10)             ! 行号十位

    ! tens digit of the row number

    call putchar(48 + row - (row / 10) * 10) ! 行号个位

    ! ones digit of the row number

    call putchar(59)        ! ;

    call putchar(51)        ! 3

    call putchar(72)        ! H

    call putchar(27)        ! ESC

    call putchar(91)        ! [

    call putchar(48 + fg / 10)              ! 色号十位

    ! tens digit of the color number

    call putchar(48 + fg - (fg / 10) * 10)  ! 色号个位

    ! ones digit of the color number

    call putchar(59)        ! ;

    call putchar(52)        ! 4

    call putchar(48)        ! 0      ⇒ 「40」黑背景

    ! 0      => "40" black background

    call putchar(109)       ! m

    if (lang == 0) print *, '前景色', c, ' 亮色,背景 = 0 黑'
    if (lang /= 0) print *, 'foreground', c, ' bright, background = 0 black'
  end do

  ! ── ④ 光标定位:先写右半段(第 21 行第 34 列)───────────────────

  ! -- ④ Cursor positioning: write the right half first (row 21, column 34) --

  call putchar(27)          ! ESC

  call putchar(91)          ! [

  call putchar(50)          ! 2

  call putchar(49)          ! 1      ⇒ 「21」行

  ! 1      => "21" row

  call putchar(59)          ! ;

  call putchar(51)          ! 3

  call putchar(52)          ! 4      ⇒ 「34」列

  ! 4      => "34" column

  call putchar(72)          ! H      ⇒ ESC[21;34H

  call putchar(27)          ! ESC

  call putchar(91)          ! [

  call putchar(57)          ! 9

  call putchar(51)          ! 3      ⇒ 「93」亮黄前景

  ! 3      => "93" bright yellow foreground

  call putchar(59)          ! ;

  call putchar(52)          ! 4

  call putchar(49)          ! 1      ⇒ 「41」红背景

  ! 1      => "41" red background

  call putchar(109)         ! m

  if (lang == 0) print *, '<- 先写的(第 34 列)'
  if (lang /= 0) print *, '<- written first (column 34)'

  ! 再回到第 21 行第 1 列写左半段

  ! Then go back to row 21, column 1 and write the left half

  call putchar(27)          ! ESC

  call putchar(91)          ! [

  call putchar(50)          ! 2

  call putchar(49)          ! 1

  call putchar(59)          ! ;

  call putchar(49)          ! 1      ⇒ ESC[21;1H

  call putchar(72)          ! H

  call putchar(27)          ! ESC

  call putchar(91)          ! [

  call putchar(57)          ! 9

  call putchar(54)          ! 6      ⇒ 「96」亮青前景

  ! 6      => "96" bright cyan foreground

  call putchar(59)          ! ;

  call putchar(52)          ! 4

  call putchar(48)          ! 0      ⇒ 「40」黑背景

  ! 0      => "40" black background

  call putchar(109)         ! m

  if (lang == 0) print *, '后写的(第 1 列)-> '
  if (lang /= 0) print *, 'written second (column 1) -> '

  ! ── ⑤ 收尾:第 23 行第 1 列,复位成白字黑底(37 / 40)──────────

  ! -- ⑤ Wrap-up: row 23, column 1, reset to white on black (37 / 40) --

  call putchar(27)          ! ESC

  call putchar(91)          ! [

  call putchar(50)          ! 2

  call putchar(51)          ! 3

  call putchar(59)          ! ;

  call putchar(49)          ! 1      ⇒ ESC[23;1H

  call putchar(72)          ! H

  call putchar(27)          ! ESC

  call putchar(91)          ! [

  call putchar(51)          ! 3

  call putchar(55)          ! 7      ⇒ 「37」白前景

  ! 7      => "37" white foreground

  call putchar(59)          ! ;

  call putchar(52)          ! 4

  call putchar(48)          ! 0      ⇒ 「40」黑背景

  ! 0      => "40" black background

  call putchar(109)         ! m

  if (lang == 0) print *, '=== done(已复位为白字黑底)==='
  if (lang /= 0) print *, '=== done (reset to white on black) ==='
end program demo_tty