Backspace(\b) 不清除非规范模式 termios 中的文本

Backspace(\b) not clearing text in Non-canonical mode termios

我正在尝试通过按退格键来清除文本,我在非规范模式下使用 termios。我创建了一个条件语句,当用户按下退格键时,它应该通过向后退一个字符来删除前一个字符。

但是当我按下 Backspace 而不是删除字符时,它会在该行打印 ^?

我不想使用规范模式。

我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h> 
#include <string.h> 
#define MAX_COMMANDS 1000
#define MAX_LENGTH 200

static struct termios initial_settings, new_settings;
static int peek_character = -1;
void init_keyboard();
void close_keyboard();
int kbhit();
int readch();

void run(char inp[]) {
    system(inp);
}

int main() {
    //65 = UP
    //66 = DOWN
    //Backspace = 127
    int ch;
    char str[MAX_COMMANDS][MAX_LENGTH];
    init_keyboard();
    int i = 0;
    int j = 0;

    while(ch != 'q') {

    if(kbhit()) {

        ch = readch();

        if (ch == 127) {
            const char delbuf[] = "\b \b";
            write(STDOUT_FILENO, delbuf, strlen(delbuf));
        }

        if (ch == 10) {

            run(str[i]);
            i++;
            j = 0;

        }  else {
            str[i][j] = ch;

            j++;
        }


    }

}
    close_keyboard();
    exit(0);
}

void init_keyboard() {
    tcgetattr(0, &initial_settings);
    new_settings = initial_settings;
    new_settings.c_lflag &= (ECHO | ECHOE | ~ICANON);
    new_settings.c_lflag &= ~ISIG;
    new_settings.c_cc[VMIN] = 1;
    new_settings.c_cc[VMIN] = 0;
    tcsetattr(0, TCSANOW, &new_settings);

}

void close_keyboard() {

    tcsetattr(0, TCSANOW, &initial_settings);

}

int kbhit() {
    char ch;
    int nread;

    if (peek_character != -1) {
        return 1;
    }

    new_settings.c_cc[VMIN] = 0;
    tcsetattr(0, TCSANOW, &new_settings);
    nread = read(0, &ch,1);
    new_settings.c_cc[VMIN]=1;
    tcsetattr(0, TCSANOW, &new_settings);

    if (nread == 1) {
        peek_character = ch;
        return 1;
    }
    return 0;
}


int readch() {
    char ch;

    if (peek_character != -1) {
        ch = peek_character;
        peek_character = -1;
        return ch;
    }

    read(0, &ch,1);
    return ch;
}


i am trying to clear text by pressing the backspace key, i am using termios in non canonical mode.

您的 termios 初始化有一些错误(这只是代码中问题的一部分)。
来自 init_keyboard() 的以下陈述是不合逻辑的:

    new_settings.c_lflag &= (ECHO | ECHOE | ~ICANON);  

推测您打算禁用 ICANON 属性(即使用非规范输入)。
您打算启用 ECHO 和 ECHOE 属性吗? (根据您代码的其他部分,我会这么认为。)

此语句不启用 ECHO 和 ECHOE 属性,但最终只是保留它们已经处于的任何状态。
这是一个潜在的错误,运气不好,可能永远不会被触发。

由于以下两个语句使用相同的索引 VMIN,其中一个不应该改为索引 VTIME 吗?

    new_settings.c_cc[VMIN] = 1;
    new_settings.c_cc[VMIN] = 0;  

i have created a conditional statement that when the users press backspace it should remove the previous character by going one character back.

请注意,您的代码仅尝试“删除显示的前一个字符”
没有尝试从输入缓冲区中“删除前一个字符”
这可能是另一个错误。

But when i press Backspace instead of removing the character it prints ^? on that line.

这似乎不是对代码功能的准确报告。

当我在 PC 终端 window(LXTerminal 0.2.0)中执行您的代码时,我最终只看到 ^ 而不是您为退格键报告的 ^?关键。
明确地说,Backspace 键是 echoed/displayed 和 ^?(因为默认情况下启用 ECHOCTL),但是您的程序会尝试执行额外的处理以响应该特定输入,结果只显示 ^.

您的代码假定每个输入字符都作为单个字符回显(即显示)。
这是一个错误的假设。
生成 ASCII 控制代码而不是可打印字符的密钥将 echoed/displayed 作为两个字符(如您所报告的那样)。
无法轻松映射到 ASCII 代码的 "special" 键(来自 PC 键盘)将被读取为转义序列,即几个(3 到 5 个或更多)字符的序列。

如果您希望 "erase" 或覆盖前一个字符以及回显的退格键,那么您的代码将需要返回(至少)三 (3) 个字符。
换句话说

        const char delbuf[] = "\b \b";

需要

        const char delbuf[] = "\b\b\b   \b\b\b";

请注意,这不是一个完整的解决方案,因为它只能 "remove" 单个前一个字符,前一个控制字符或转义序列将无法正确处理。
这个答案只是为了解释为什么你没有得到你期望的结果。


请注意,本地回声(由 termios)的使用可能被认为是问题的一部分,因为您似乎没有掌握后果。
非规范模式通常会抑制任何自动回显,以便简化控制字符和转义序列的处理。

仅供参考,如果没有自动回显,那么您尝试使用的 backspace, space, backspace 序列可能有效(因为只有前一个字符要覆盖,并且不会有退格键的回显)。


请注意,您的代码使用 busy wait 到 check/retrieve 键盘输入(因为 VTIME 可能设置为 0 且 VMIN 为 0)。
这是浪费 CPU 个周期的 低效的输入方法,并且在电池供电的系统上耗尽电池的速度比必要的更快。
即使您添加了延迟,代码仍然会低效地 轮询 数据的系统缓冲区,而不是利用由OS。