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

前端 未结 2 600
无人共我
无人共我 2021-02-10 07:52

So I have the following code which basically just reads characters user inputs and prints them until \'q\' is entered.

#include
#include

        
相关标签:
2条回答
  • 2021-02-10 08:29

    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.

    0 讨论(0)
  • 2021-02-10 08:44

    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 :)

    0 讨论(0)
提交回复
热议问题