尽管`poll()`返回了“好”值,但为什么`getchar()`挂起?

问题描述 投票:0回答:1

我正在用 C 语言制作一个文本编辑器,并使用

poll()
函数来确定输入是否准备就绪(如果没有,则继续使用本例中未显示的其他功能。然后,如果有字符可用(即
 if (!poll(...))
条件为假),用
getchar()
收集并处理。代码如下:

#include <poll.h>
#include <stdio.h>

int main(void) {

        input_set_tty_raw();
        
        char input_history[1000];
        int curr = 0;
        struct pollfd in = {.fd = 0, .events = POLLIN};
        
        while (1) {
                if (!poll(&in, 1, 20)) {  // arbitrary timeout for poll function
                        continue;
                }
                char c = getchar();

                /* problem code */
                if (input_history[curr - 1] == '\033' && c == '[') {
                        while (poll(&in, 1, 0)) {
                                getchar();
                        }
                        return input_restore_tty();
                }

                // handle inputs
                input_history[curr++] = c;
        }

        return input_restore_tty();
}

如果您想知道,

input_set_tty_raw()
相当于运行
$ stty raw
,允许逐个字符地纯(或
raw
)处理输入。
input_restore_tty
函数将终端返回到原始状态。

当按下箭头键等键时,会传递到

stdin
的特殊“转义序列”会出现问题。例如,当按下箭头键时,会传入一系列字节
27
-
91
-
6x
。您可以使用这个小实用程序来检查此行为:

#include <stdio.h>

int main(void) {
        input_set_tty_raw();
        while (1) {
                char c = getchar();
                if (c == 'q') {
                        return input_restore_tty();
                }
                printf("%d\n\r", c);
        }
}

前面提到的

27
-
91
-
6x
行为似乎与其他转义序列共享
27
-
91
标头(这是一个
"\033["
),例如按删除(而不是退格)键在键盘上输入
27
-
91
-
51
-
126

因此,在标记为

problem code
的第二个条件中,我检查先前输入的字符是否为
\033
转义字符以及当前字符是否为
91
(或
[
)字符。如果是这样,我使用
while
语句让所有其他输入通过。

从测试来看,

\033
[
之间的延迟似乎不为零,而转义序列中的其余字符似乎是立即输入的,因此内部
poll
的超时为0 .我在条件中放置了一个
return
语句,以准确显示何时处理所有输入。

但是,这段代码似乎挂起。编译并运行它,您可以看到当您按箭头键时代码不会像预期的那样返回。但是,当您在此之后再按另一个键时,它会返回,表明程序挂在

getchar()
块内的
problem code
函数中。

据我所知,

poll()
仅在有可用输入时返回非零值。那么,为什么它挂起,我该如何解决这个问题?

如果需要,辅助函数如下所示,及其所需的包含内容。

#include <termios.h>

static struct termios tbufsave;
int input_set_tty_raw(void) {
        struct termios tbuf;
        if (tcgetattr(0, &tbufsave) == -1) {
                return 1;
        }

        tbuf = tbufsave;
        tbuf.c_iflag &= ~(INLCR | ICRNL | ISTRIP | IXON | BRKINT);
        tbuf.c_oflag &= ~OPOST;
        tbuf.c_lflag &= ~(ICANON | ISIG | ECHO);
        tbuf.c_cc[VMIN] = 1;
        tbuf.c_cc[VTIME] = 0;

        return tcsetattr(0, TCSANOW, &tbuf) == -1;   // 1 for error
}

int input_restore_tty(void) {
        return tcsetattr(0, TCSANOW, &tbufsave) == -1;   // 1 for error
}
c unix tty
1个回答
0
投票

将评论转换为答案。

是的,使用

input_set_tty_raw()
意味着终端驱动程序立即返回字符,但是您还需要让标准I/O包知道它不应该缓冲字符。您可能会使用
setvbuf()
来完成此操作。

或者,使用

read()
而不是标准 I/O 来收集数据。

© www.soinside.com 2019 - 2024. All rights reserved.