C - 如何在没有缓冲的情况下轮询()输入?

C - how to poll() input without buffering?

我正在尝试检测输入到标准输入的任何字符(没有换行符)。

我试过了:

setvbuf(stdin, NULL, _IONBF); //This returns 0
struct pollfd pfd = {STDIN_FILENO, POLLIN};

while (!poll(pfd, 1, ms)) {
  /* do some thing, e.g. printf("n\n"); */
}

当我输入 q 时似乎没有停止打印,但在我按下 enter 后确实停止了。我正在使用的系统是 arch-linux,编译器是 gcc.

q 内核的 TTY 层 driver/buffer 中被阻止,因为它处于 "cooked" 模式。

在此模式下,当 驱动程序 看到换行符时,它只会 return 向应用程序发送内容。然后返回:q\n(即 q<newline>)。

要立即在 任何 字符上使用 return,您必须使用 ioctl 调用将 TTY 层放入 "raw"模式。

您需要使用[推荐的] termios 调用:tcgetattrtcsetattr


更新:

Will ioctl alone works? Which command is corresponding to change terminal into raw mode?

看看man termios。它有关于如何设置原始模式的完整文档(在手册页中称为 "non-canonical" 模式)。

我已经有一段时间没做这个了,但这是一个骨架函数。

虽然该函数在最后恢复原始状态,但您可能希望在程序启动时设置一次非规范模式一次

但是,您必须自己处理所有需要正常规范行编辑的程序其他部分的行编辑(例如支持退格等)。

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

void
change_tty(int fd)
{
    struct termios orig;
    struct termios raw;

    // get original cooked/canonical mode values
    tcgetattr(fd,&orig);

    // set options for raw mode
    raw = orig;
#if 0
    raw.c_lflag &= ~ICANON;
    raw.c_cc[VMIN] = ...
    raw.c_cc[VTIME] = ...
#else
    cfmakeraw(&raw);
#endif

    // put unit into raw mode ...
    tcsetattr(fd,TCSANOW,&raw);

    // do stuff in raw mode ...

    // restore original mode
    tcsetattr(fd,TCSANOW,&orig);
}

这对我有用,但可能取决于您的 system/terminal

#include <stdio.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>

int main() {
    int i = 0;
    struct termios ts;
    tcgetattr(0, &ts);
    ts.c_lflag &= ~ICANON;
    tcsetattr(0, TCSANOW, &ts);
    while (!ioctl(0, FIONREAD, &i) && !i) {
        printf("x");
        fflush(stdout);
        sync();
        usleep(100);
    }
    printf("\n");
    return 0;
}

克雷格真的为你解答了这个问题。我只是好奇地想找到一个实现。 ioctl(0, FIONREAD, &i) 获取缓冲区中的字符数并将其放入 i。 termios 和 ioctl_tty 的手册页包含提出此解决方案所需的所有详细信息。

不过,老实说,如果您想制作这样的交互式内容,ncurses 会使它变得更简单。