我正在用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函数将终端返回到原始状态.

当按下箭头键等键时,特殊的"转义序列"会出现问题.例如,当按下箭头键时,字节序列27916x被传入.你可以使用这个小工具来判断这个行为:

#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);
        }
}

前面提到的27916x行为似乎与其他转义序列共享2791标题(即"\033["),例如在键盘上按下删除键(而不是退格键)输入279151126.

因此,在标记为problem code的第二个条件中,我判断输入的前一个字符是否是这个\033转义字符,当前字符是否是91(或[)字符.如果是,我让所有其他输入通过使用while语句.

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

然而,这个代码似乎挂起了.编译并运行此代码,您可以看到当您按下箭头键时代码不会返回,这是预期的.但是,当您在此之后按下另一个键时,它会返回,这表明程序挂在problem code块内的getchar()函数中.

据我所知,poll()只在有可用输入时返回非零值.为什么它挂了,我该怎么解决这个问题?

如果需要的话,下面列出了帮助函数,其中包含了它们的required include.

#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
}

推荐答案

Transferring a comment into an answer.

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

或者,使用read()而不是标准I/O来收集数据.

C++相关问答推荐

设计处理各种数据类型的方法和数据 struct

自定义应用程序上的日志(log)轮换问题

无法用C++编译我的单元测试

在C++中通过空指针隐式访问常量变量的值

_泛型控制表达式涉及数组碰撞警告的L值转换错误?

GCC创建应用于移动项的单独位掩码的目的是什么?

如何在C中打印包含扫描字符和整数的语句?

用C++从外部ELF符号读取值

在libwget中启用Cookie会导致分段故障

如何在C++中安全地进行浮点运算

将变量或参数打包到 struct /联合中是否会带来意想不到的性能损失?

Wcstok导致分段故障

如何在c中使用具有不同变量类型的内存分配?

如何使这个While循环在新行上结束

从BIOS(8086)中读取刻度需要多少?

与外部SPI闪存通信时是否应禁用中断?

STM32:代码的执行似乎取决于它在闪存中的位置

const struct 成员的 typedef 中的灵活数组大小

比 * 更快的乘法

Clang 是否为内联汇编生成了错误的代码?