Search code examples
cshellgetchararrow-keys

Arrow keys control in a custom shell using C language


I'm trying to make arrow keys move between characters in a single line (left + right) and between commands from history (up + down) for a custom shell (semester project).

at this point when hitting one of the arrows ^[[A, ^[[B, ^[[C or ^[[D is displayed and after hitting enter I recognize that one of them is hit using:

char a = getchar();

if (a == '\033') {
    getchar();
    int ch2 = getchar();
    switch(ch2){
        case 'A': 
            printf("UP\n"); 
            break;
        case 'B': 
            printf("DOWN\n"); 
            break;
        case 'D': 
            printf("LEFT\n"); 
            break;
        case 'C': 
            printf("RIGHT\n"); 
            break;
        default:
            printf("SOME OTHER SCROLL KEY PRESSED: %d %d\n", a, ch2); 
            break;
    }
}

What I want to get is that as soon as a I hit one of the arrows the action happens without displaying anything.


Solution

  • By default, terminal input in unix systems are line-buffered, you can use termios to specify your own return condition for stdin functions:

    #include <stdio.h>
    #include <termios.h>
    
    static struct termios orig_termios;
    
    char get_char_wait_for_keypress(void) {
        struct termios raw;
        // Get stdin file descriptor (0 by default)
        int stdin_fileno = fileno(stdin);
        // Copy terminal io settings
        raw = orig_termios;
        // Set return condition at first byte being received (For input timeout you can use `raw.c_cc[VTIME] = secs`)
        raw.c_cc[VMIN] = 1;
        // Apply settings with new return condition
        tcsetattr(stdin_fileno, TCSANOW, &raw);
        // Get char with new conditions
        char c = getchar();
        // Restore old settings
        tcsetattr(stdin_fileno, TCSANOW, &orig_termios);
        return c;
    }
    
    int main(void) {
    struct termios raw;
        char c = get_char_wait_for_keypress();
        printf("%d", c);
    }