C - how to poll() input without buffering?

天涯浪子 提交于 2021-01-29 07:54:12

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!