How to make arrow keys and backspace work correctly when asking input from user in C program using termios.h?

落爺英雄遲暮 提交于 2019-12-03 04:44:22
mozzbozz

have you looked into the man pages? (should be man termios or look somewhere online)

There I found the ECHOE flag which is said to have the following effect:

If ICANON is also set, the ERASE character erases the preceding input character, and WERASE erases the preceding word.

This should fix your backspace problem?

I also suggest, you have a look into the examples in the man page. E.g. you could do the following:

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

...to set more than one flag at a time in only one line. I know the man pages are hard to read for beginners but you will get used to them and the more you use them, the more efficient they become for looking up C/POSIX-functions etc (just in case, you don't use them anyway).

The arrow-key-problem: Maybe you can try the cfmakeraw()-function; its description sounds promising. I haven't had time to investigate any further about the arrow keys. However, maybe you find something else useful in the man page.

BTW: termios looks interesting, I always wondered which functions certain command line programmes are using; learned something by your question, thanks!

EDIT

I've done some more research this weekend. The "strange" symbol printed when pressing the backspace key is quite easy to hide. It is the ASCII-value 0x7f. So add a simple

if (c == 0x7f) { continue; }

...to just ignore the backspace key. Or handle it in a way to remove the last character (see code example below).

This simple workaround doesn't work for the arrow keys though as they are no ASCII characters :/ However, these two topics helped me handling also this problem: topic 1 and topic 2. Basically pressing the arrow keys results in a sequence of a couple of chars being sent to stdin (see the second link for more information).

Here is my complete code which (I think) works the way you wish:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>

int main(void) {
    char c;
    static struct termios oldtio, newtio;
    tcgetattr(0, &oldtio);
    newtio = oldtio;
    newtio.c_lflag &= ~ICANON;
    newtio.c_lflag &= ~ECHO;
    tcsetattr(0, TCSANOW, &newtio);

    printf("Give text:\n");
    fflush(stdout);
    while (1) {
        c = getchar();

        // is this an escape sequence?
        if (c == 27) {
            // "throw away" next two characters which specify escape sequence
            c = getchar();
            c = getchar();
            continue;
        }

        // if backspace
        if (c == 0x7f) {
            // go one char left
            printf("\b");
            // overwrite the char with whitespace
            printf(" ");
            // go back to "now removed char position"
            printf("\b");
            continue;
        }

        if (c == 'q') {
            break;
        }
        printf("%c", c);
    }
    printf("\n");
    tcsetattr(0, TCSANOW, &oldtio);

    return 0;
}

BTW you can get the complete escape sequences by the following code:

int main(void) {
    char c;
    while (1) {
        c = getchar();
        printf("%d", c);
    }
    return 0;
}

I think I don't have to say that this complete thing is quite a dirty hack and it's easy to forget handling some special keys. E.g. in my code I don't handle the page-up/down or home keys... --> the code above is far from complete but gives you a point to start. You should also have a look at terminfo which can provide you a lot of the necessary information; it should also help with a more portable solution. As you see, this "simple" thing can become quite complex.So you might rethink your decision against ncurses :)

Actually, for handling the arrow keys, you would have to implement a good-sized chunk of ncurses. There are pros/cons: the main drawback to using ncurses in a command-line application might be that it usually clears the screen. However, (n)curses provides a function filter. There is a sample program "test/filter.c" in the ncurses source which illustrates this by using the left-arrow key as an erase character, and passes the resulting line to system() to run simple commands. The sample is less than 100 lines of code -- simpler and more complete than the examples above, it seems.

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