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
调用:tcgetattr
和 tcsetattr
更新:
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 会使它变得更简单。
我正在尝试检测输入到标准输入的任何字符(没有换行符)。
我试过了:
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
调用:tcgetattr
和 tcsetattr
更新:
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 会使它变得更简单。