如何让 `cin` 读取原始模式终端

How to get `cin` to read a raw mode terminal

为什么标准流可以在规范模式下从终端接收输入,但是你把它放在原始模式下,突然这种方法就不再有效了?我很了解 POSIX 串行编程,通常您使用 read。我正在尝试更好地理解标准流。

#include <termios.h>
#include <unistd.h>
#include <iostream>

termios original;

void enableRawMode() {
  tcgetattr(STDIN_FILENO, &original);
  termios raw = original;
  cfmakeraw(&raw);
  raw.c_cc[VMIN] = 0;
  raw.c_cc[VTIME] = 1;
  tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}

int main() {
  enableRawMode();

  char c;

  // This works as expected.
  //int nread;
  //while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
  //  if (nread == -1 && errno != EAGAIN) {
  //    break;
  //  }
  //}

  // This loops forever, the failbit is always true, gcount is always 0.
  while (!(std::cin.get(c))) {
    if (std::cin.bad() || std::cin.eof()) {
      break;
    }

    std::cin.clear();
  }

  tcsetattr(STDIN_FILENO, TCSAFLUSH, &original);
}

经过多次讨论和一些调查,我发现这是对给定标准流的实施的限制。基本上,我推断当您的终端处于熟化模式时标准流工作正常,但如果处于原始模式则不可靠。

代码可以简化为使用 std::cin.rdbuf()->sbumpc(),在本例中将调用 uflow,这可能会调用 underflow,后者将从底层设备获取数据.与标准流关联的流缓冲区是实现定义的。

第一次调用 sbumpc,更具体地说,最终 underflow 将在从标准输入读取时启动与 VTIME 关联的计时器。如果在超时内收到输入,cin 将按预期工作。但是如果发生超时,stream buffer会进入undefined状态,sbumpc永远return EOF.

状态与流相关联,而不是流缓冲区,因此调用 std::cin.clear() 会清除状态标志,但不会更正流缓冲区中的潜在问题,流缓冲区仍处于损坏状态。

可能有一个重置搜索位置的解决方案,但同样,给定的标准流是实现定义的,不知道它们可能拥有什么内部状态,或者如果在一个平台上成功,如果它在另一个平台。据我所知,没有涵盖这种情况的文档。

两种可能的解决方案是直接使用 POSIX API 并喜欢它,或者编写您自己的使用 POSIX API 的流实现。