问题
I am trying to detect any character typed to stdin (without a newline character).
I tried :
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"); */
}
It appears not stop printing when I typed q, but did stop after I hit enter. The system I am working on is arch-linux, compiler is gcc.
回答1:
The q
is being held up in the kernel's TTY layer driver/buffer because it is in "cooked" mode.
In this mode, it will only return something to the app when the driver sees a newline. It then gives back: q\n
(i.e. q<newline>
).
To have it return immediately on any character, you'll have to use ioctl
calls to put the TTY layer into "raw" mode.
You'll need to use [recommended] the termios
calls: tcgetattr
and tcsetattr
UPDATE:
Will ioctl alone works? Which command is corresponding to change terminal into raw mode?
Look at man termios
. It has full documentation for how to set raw mode (what is called "non-canonical" mode in the man page).
It's been a while since I've done this, but here's a skeleton function.
Although the function restores original state at the end, you might want to set non-canonical mode once at program start.
But, then, you'll have to handle all your own line editing (e.g. supporting backspace, etc.) for other sections of your program that want normal canonical line editing.
#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);
}
回答2:
This works for me but it may depend on your 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;
}
Craig really answered this for you. I was just curious enough to find an implementation. The ioctl(0, FIONREAD, &i) gets the number of characters in the buffer and puts it into i. The man pages for termios and ioctl_tty contained all of the details needed to come up with this solution.
Honestly, though, if you are wanting to make something interactive like this, ncurses makes it somewhat simpler.
来源:https://stackoverflow.com/questions/54358053/c-how-to-poll-input-without-buffering