Writing a “real” interactive terminal program like vim, htop, … in C/C++ without ncurses

前端 未结 3 387
一整个雨季
一整个雨季 2021-01-30 02:34

No, I don\'t want to use ncurses, because I want to learn how the terminal works and have fun programming it on my own. :) It doesn\'t have t

3条回答
  •  陌清茗
    陌清茗 (楼主)
    2021-01-30 02:59

    Although this is question a bit old, I thought I should share a short example of how to do this without using ncurses, it's not difficult but I'm sure it won't be as portable.

    This code sets stdin in raw mode, switches to an alternate buffer screen (which saves the state of the terminal before launching it), enables mouse tracking and prints the button and the coordinates when the user clicks somewhere. After quitting with Ctrl+C the program reverts the terminal configuration.

    #include 
    #include 
    #include 
    
    int main (void)
    {
        unsigned char buff [6];
        unsigned int x, y, btn;
        struct termios original, raw;
    
        // Save original serial communication configuration for stdin
        tcgetattr( STDIN_FILENO, &original);
    
        // Put stdin in raw mode so keys get through directly without
        // requiring pressing enter.
        cfmakeraw (&raw);
        tcsetattr (STDIN_FILENO, TCSANOW, &raw);
    
        // Switch to the alternate buffer screen
        write (STDOUT_FILENO, "\e[?47h", 6);
    
        // Enable mouse tracking
        write (STDOUT_FILENO, "\e[?9h", 5);
        while (1) {
            read (STDIN_FILENO, &buff, 1);
            if (buff[0] == 3) {
                // User pressd Ctr+C
                break;
            } else if (buff[0] == '\x1B') {
                // We assume all escape sequences received 
                // are mouse coordinates
                read (STDIN_FILENO, &buff, 5);
                btn = buff[2] - 32;
                x = buff[3] - 32;
                y = buff[4] - 32;
                printf ("button:%u\n\rx:%u\n\ry:%u\n\n\r", btn, x, y);
            }
        }
    
        // Revert the terminal back to its original state
        write (STDOUT_FILENO, "\e[?9l", 5);
        write (STDOUT_FILENO, "\e[?47l", 6);
        tcsetattr (STDIN_FILENO, TCSANOW, &original);
        return 0;
    }
    

    Note: This will not work properly for terminals that have more than 255 columns.

    The best references for escape sequences I've found are this and this one.

提交回复
热议问题