Backspace(\b) 在非规范模式 termios 中不清除文本 [英] Backspace(\b) not clearing text in Non-canonical mode termios

查看:49
本文介绍了Backspace(\b) 在非规范模式 termios 中不清除文本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

我不想使用规范模式.

我的代码:

#include #include #include #include #include #include #define MAX_COMMANDS 1000#define MAX_LENGTH 200静态结构 termios initial_settings, new_settings;静态 int peek_character = -1;void init_keyboard();void close_keyboard();int kbhit();int readch();无效运行(字符输入[]){系统(输入);}int main() {//65 = 向上//66 = 向下//退格= 127国际频道;字符 str[MAX_COMMANDS][MAX_LENGTH];init_keyboard();int i = 0;int j = 0;while(ch != 'q') {如果(kbhit()){ch = 读取();如果 (ch == 127) {const char delbuf[] = "\b \b";写(STDOUT_FILENO,delbuf,strlen(delbuf));}如果 (ch == 10) {运行(str [i]);我++;j = 0;}  别的 {str[i][j] = ch;j++;}}}close_keyboard();退出(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() {字符 ch;int nread;如果(peek_character != -1){返回 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);如果(nread == 1){peek_character = ch;返回 1;}返回0;}int readch() {字符 ch;如果(peek_character != -1){ch = peek_character;peek_character = -1;返回 ch;}读(0,&ch,1);返回 ch;}

解决方案

我试图通过按退格键来清除文本,我在非规范模式下使用 termios.

您的 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;

<小时><块引用>

我创建了一个条件语句,当用户按下退格键时,它应该通过返回一个字符来删除前一个字符.

请注意,您的代码只会尝试删除前一个字符".
不会尝试从输入缓冲区删除前一个字符".
这可能是另一个错误.

<块引用>

但是当我按 Backspace 而不是删除它打印的字符时 ^?在那条线上.

这似乎不是您的代码功能的准确报告.

当我在 PC 终端窗口 (LXTerminal 0.2.0) 中执行您的代码时,我最终只看到 ^ 而不是您报告的 ^?退格键.
需要明确的是,退格键被回显/显示为 ^?(因为默认情况下启用 ECHOCTL),但您的程序尝试执行额外的处理以响应该特定输入,并导致显示只是^.

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

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

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

必须

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

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

<小时>

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

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

<小时>

请注意,您的代码使用忙等待来检查/检索键盘输入(因为 VTIME 可能设置为 0,而 VMIN 为 0).
这是浪费 CPU 周期的输入方法,而且在电池供电的系统上消耗电池的速度比必要的要快.
即使您添加了延迟,代码仍然会低效地轮询系统缓冲区中的数据,而不是利用操作系统提供的高效事件驱动功能.

i am trying to clear text by pressing the backspace key, i am using termios in non canonical mode. 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.

I don't want to use canonical mode.

My Code:

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

Your termios initialization has a few bugs (which are just part of the problems in the code).
The following statement from init_keyboard() is illogical:

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

Presumably you intended to disable the ICANON attribute (i.e. use non-canonical input).
Is your intent to enable the ECHO and ECHOE attributes? (I would think so based on other parts of your code.)

This statement does not enable the ECHO and ECHOE attributes, but ends up merely preserving whatever state they are already in.
This is a latent bug that is, by dumb luck, likely never to be triggered.

Since both of the following statements use the same index, VMIN, shouldn't one of them be indexing VTIME instead?

    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.

Note that your code only tries to "remove the previous character" that is displayed.
There is no attempt to "remove the previous character" from the input buffer.
This is probably another bug.

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

That doesn't seem to be an accurate reporting of what your code does.

When I executed your code in a PC terminal window (LXTerminal 0.2.0), I end up seeing just ^ rather than the ^? that you report for the Backspace key.
To be clear, the Backspace key is echoed/displayed as ^? (because ECHOCTL is enabled by default), but your program tries to perform additional processing in response to that particular input, and that results in displaying just ^.

Your code assumes that each input character is echoed (i.e. displayed) as a single character.
That's a faulty assumption.
A key that generates an ASCII control code instead of a printable character will be echoed/displayed as two characters (as you reported).
A "special" key (from a PC keyboard) that doesn't readily map to an ASCII code will be read as an escape sequence, i.e. a sequence of several (3 to 5 or more) characters.

If you expect to "erase" or overwrite the previous character as well as the echoed Backspace, then your code would need to go back (at least) three (3) characters.
In other words

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

needs to be

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

Note that this is not a complete solution because it can only "remove" a single previous character, and a previous control character or an escape sequence will not be handled correctly.
This answer is only to explain why you are not getting the result you expect.


Be aware that the use of local echo (by termios) could be considered part of the problem since you don't seem to grasp the ramifications.
Non-canonical mode typically suppresses any automatic echoing so that handling of control characters and escape sequences is simplified.

FYI if there was no automatic echo, then the backspace, space, backspace sequence you tried to use could be effective (because there is only the one previous character to overwrite and there would be no echo of the Backspace).


Note that your code uses a busy wait to check/retrieve the keyboard input (because VTIME is likely set to 0 and VMIN is 0).
This is the most inefficient method of input that wastes CPU cycles, and on a battery-powered system drains the battery faster than necessary.
Even if you added a delay, the code would still be inefficiently polling the system buffer for data instead of utilizing the efficient event-driven capabilities provided by the OS.

这篇关于Backspace(\b) 在非规范模式 termios 中不清除文本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆