如何读取 Swift 中的 ANSI Escape 代码响应值?

How to read ANSI Escape code response value in Swift?

我正在使用 Swift 作为控制台应用程序来处理 ANSI 代码(ESC 序列)。发送 ESC 命令很简单,例如设置文本颜色。但是,从 ESC 命令读取响应值具有挑战性。这是一个简单的测试程序:

print("Get cursor position:", "\u{1b}[6n", terminator: "")
let s = readLine()!
print(s)

程序发送<ESC>[6n获取当前光标位置,控制台将return<ESC>[<line>;<column>R字符串。以下是问题:

  1. readLine() 一直等待输入,直到用户按下 Return 或回车键。我以为输入缓冲区变空后它会自动停止读取。
  2. readLine() 奇怪的是似乎没有从控制台读取响应值,尽管它清楚地打印在屏幕上。发生什么事了?
  3. 响应值打印在控制台上。我想静静地拥有它,就像 print() 打印 ESC 命令的方式一样。有没有办法暂时将标准输入重定向到变量?

系统:
• MacOS Mojave
• XCode 10
• Swift 4.2
• 运行 在终端应用程序上

我一直在查看 GitHub 和 Google 以找到一些答案,但我没有找到答案。那么,这里有人会提示我从哪里开始解决这个问题吗?谢谢。

此致,

~蜜蜂

终端默认处于“规范输入处理”模式,这意味着读取请求不会return直到整行被键入和处理。 tcsetattr() 函数用于禁用输入处理和回显,例如参见 [​​=17=]

  • Reading the Device Status Report ANSI escape sequence reply

以及 tcsetattr(3)termios(4) 手册页。

这里是 Swift 中的一个简单示例,灵感来自上述问答中的 C 代码,以及 Xcode Swift Command Line Tool reads 1 char from keyboard without echo or need to press return:

#if os(Linux)
import Glibc
#else
import Darwin
#endif

// Write escape sequence:
write(STDOUT_FILENO, "\u{1b}[6n", 4)

// Save terminal attributes:
var oldt = termios()
tcgetattr(STDIN_FILENO, &oldt)

// Disable canonical input processing and echo:
var newt = oldt
newt.c_lflag &= ~tcflag_t(ICANON)
newt.c_lflag &= ~tcflag_t(ECHO)
tcsetattr(STDIN_FILENO, TCSANOW, &newt)

// Read response:
var response: [UInt8] = []
var c: UInt8 = 0
repeat {
    read(STDIN_FILENO, &c, 1)
    response.append(c)
} while c != UInt8(ascii: "R")

// Restore original terminal attributes:
tcsetattr(STDIN_FILENO, TCSANOW, &oldt)

print(response) // [27, 91, 52, 59, 49, 82] == ESC[4;1R

请注意,这仅在 运行 在终端 window 中有效,而不是在 Xcode.