How do I make the arrow keys and backspace work properly when prompting for input from the user in a C program using termios.h?

So I have the following code, which basically just reads the characters of the custom inputs and prints them before typing "q".

#include<stdio.h>
#include<stdlib.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: ");
    fflush(stdout);
    while (1) {
        read(0, &c, 1);
        printf("%c", c);
        fflush(stdout);
        if (c == 'q') { break; }
    }
    printf("\n"); 
    tcsetattr(0, TCSANOW, &oldtio);

    return 0;
}

      

At the start of the main function, I turn canonical mode off so that the user can see his input when he gave it. I also turned off echo so that for example "^ [[A] does not appear when pressing the up arrow key. It works, but I can also move the cursor to the top lines in the terminal window, which is not good. Is there a way fix this so that the user can only navigate on the current line?

Another problem is backspace. When I click it, the program prints out a strange character (which I assume is 0x7f) instead of erasing the character left up to the current cursor location. I have to somehow handle the exit of the backspace key in the program with ease, but I don't know how to do it as it's a weird hexadecimal number. Any tips for this?

One option that I also thought about is to use canonical mode so that the arrow keys and backspace functions are automatically used. Canonical mode, however, works line by line, so no text appears until the user deletes "Enter". Until I figured out how to make the user see his input while typing. Is it possible?

And no ncurses or readline suggestions please. I want to do this with termios.h.

+3


source to share


2 answers


Have you looked at the man pages? (must be man termios

or watch somewhere online )

There I found a flag ECHOE

that is said to have the following effect:

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

Should this fix the backspace issue?

I also suggest you check out the examples on the man page. For example. you can do the following:

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

      

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

Arrow problem . Perhaps you can try the cfmakeraw()

-function; his description sounds promising. I didn't have time to explore the arrow keys anymore. However, you might find something else useful on the man page.

BTW: termios

looks interesting, I've always wondered what functions some command line programs use; learned something about your question, thanks!

EDIT



I did some more research this weekend. The strange character typed on the back key is fairly easy to hide. This is an ASCII value 0x7f

. So add a simple

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

      

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

This simple workaround does not work for arrow keys, although they are not ASCII characters: / However, these two topics helped me with this issue too: topic 1 and topic 2 . Basically pressing the arrow keys results in a sequence of multiple characters being sent to stdin

(see the second link for more information).

Here's my complete code, which (I think) works as 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;
}

      

By the way, you can get full escape sequences with the following code:

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

      

I think I don’t need to say that this complete thing is a rather dirty hack and it’s easy to forget to handle some special keys. For example. in my code i am not handling keys page-up/down

or home

... -> the above code is far from complete, but gives you a start. You should also take a look at terminfo

which can provide you with a lot of the information you need; this should also help with a more portable solution. As you can see, this "simple" thing can get quite complicated. So you can reconsider your solution against ncurses :)

+4


source


Actually, to handle the arrow keys you will need to implement a large ncurses chunk. There are pros and cons: the main disadvantage of using ncurses in an application from the command line can be that it usually clears the screen. However (n) curses provides a filter function . The ncurses source has a sample program "test / filter.c" that illustrates this by using the left arrow key as an erase character and passes the resulting string to system () to run simple commands. Example: Less than 100 lines of code is simpler and more complete than the examples above.



0


source







All Articles