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

旧巷老猫 提交于 2021-02-17 02:17:29

问题


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



回答1:


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.



来源:https://stackoverflow.com/questions/58205140/backspace-b-not-clearing-text-in-non-canonical-mode-termios

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