int guac_terminal_csi()

in src/terminal/terminal-handlers.c [702:1305]


int guac_terminal_csi(guac_terminal* term, unsigned char c) {

    /* CSI function arguments */
    static int argc = 0;
    static int argv[16] = {0};

    /* Sequence prefix, if any */
    static char private_mode_character = 0;

    /* Argument building counter and buffer */
    static int argv_length = 0;
    static char argv_buffer[256];

    /* Digits get concatenated into argv */
    if (c >= '0' && c <= '9') {

        /* Concatenate digit if there is space in buffer */
        if (argv_length < sizeof(argv_buffer)-1)
            argv_buffer[argv_length++] = c;

    }

    /* Specific non-digits stop the parameter, and possibly the sequence */
    else if ((c >= 0x40 && c <= 0x7E) || c == ';') {

        int i, row, col, amount;
        bool* flag;

        /* At most 16 parameters */
        if (argc < 16) {

            /* Finish parameter */
            argv_buffer[argv_length] = 0;
            argv[argc++] = guac_terminal_parse_numeric_param(argv_buffer);

            /* Prepare for next parameter */
            argv_length = 0;

        }

        /* Handle CSI functions */ 
        switch (c) {

            /* @: Insert characters (scroll right) */
            case '@':

                amount = argv[0];
                if (amount == 0) amount = 1;

                /* Scroll right by amount */
                if (term->cursor_col + amount < term->term_width)
                    guac_terminal_copy_columns(term, term->cursor_row,
                            term->cursor_col, term->term_width - amount - 1,
                            amount);

                /* Clear left */
                guac_terminal_clear_columns(term, term->cursor_row,
                        term->cursor_col, term->cursor_col + amount - 1);

                break;

            /* A: Move up */
            case 'A':

                /* Get move amount */
                amount = argv[0];
                if (amount == 0) amount = 1;

                /* Move cursor */
                guac_terminal_move_cursor(term,
                        term->cursor_row - amount,
                        term->cursor_col);

                break;

            /* B: Move down */
            case 'e':
            case 'B':

                /* Get move amount */
                amount = argv[0];
                if (amount == 0) amount = 1;

                /* Move cursor */
                guac_terminal_move_cursor(term,
                        term->cursor_row + amount,
                        term->cursor_col);

                break;

            /* C: Move right */
            case 'a':
            case 'C':

                /* Get move amount */
                amount = argv[0];
                if (amount == 0) amount = 1;

                /* Move cursor */
                guac_terminal_move_cursor(term,
                        term->cursor_row,
                        term->cursor_col + amount);

                break;

            /* D: Move left */
            case 'D':

                /* Get move amount */
                amount = argv[0];
                if (amount == 0) amount = 1;

                /* Move cursor */
                guac_terminal_move_cursor(term,
                        term->cursor_row,
                        term->cursor_col - amount);

                break;

            /* E: Move cursor down given number rows, column 1 */
            case 'E':

                /* Get move amount */
                amount = argv[0];
                if (amount == 0) amount = 1;

                /* Move cursor down, reset to column 1 */
                guac_terminal_move_cursor(term,
                        term->cursor_row + amount,
                        0);

                break;

            /* F: Move cursor up given number rows, column 1 */
            case 'F':

                /* Get move amount */
                amount = argv[0];
                if (amount == 0) amount = 1;

                /* Move cursor up , reset to column 1 */
                guac_terminal_move_cursor(term,
                        term->cursor_row - amount,
                        0);

                break;

            /* G: Move cursor, current row */
            case '`':
            case 'G':
                col = argv[0]; if (col != 0) col--;
                guac_terminal_move_cursor(term, term->cursor_row, col);
                break;

            /* H: Move cursor */
            case 'f':
            case 'H':

                row = argv[0]; if (row != 0) row--;
                col = argv[1]; if (col != 0) col--;

                guac_terminal_move_cursor(term, row, col);
                break;

            /* J: Erase display */
            case 'J':
 
                /* Erase from cursor to end of display */
                if (argv[0] == 0)
                    guac_terminal_clear_range(term,
                            term->cursor_row, term->cursor_col,
                            term->term_height-1, term->term_width-1);
                
                /* Erase from start to cursor */
                else if (argv[0] == 1)
                    guac_terminal_clear_range(term,
                            0, 0,
                            term->cursor_row, term->cursor_col);

                /* Entire screen */
                else if (argv[0] == 2 || argv[0] == 3)
                    guac_terminal_clear_range(term,
                            0, 0, term->term_height - 1, term->term_width - 1);

                break;

            /* K: Erase line */
            case 'K':

                /* Erase from cursor to end of line */
                if (argv[0] == 0)
                    guac_terminal_clear_columns(term, term->cursor_row,
                            term->cursor_col, term->term_width - 1);

                /* Erase from start to cursor */
                else if (argv[0] == 1)
                    guac_terminal_clear_columns(term, term->cursor_row,
                            0, term->cursor_col);

                /* Erase line */
                else if (argv[0] == 2)
                    guac_terminal_clear_columns(term, term->cursor_row,
                            0, term->term_width - 1);

                break;

            /* L: Insert blank lines (scroll down) */
            case 'L':

                amount = argv[0];
                if (amount == 0) amount = 1;

                guac_terminal_scroll_down(term,
                        term->cursor_row, term->scroll_end, amount);

                break;

            /* M: Delete lines (scroll up) */
            case 'M':

                amount = argv[0];
                if (amount == 0) amount = 1;

                guac_terminal_scroll_up(term,
                        term->cursor_row, term->scroll_end, amount);

                break;

            /* P: Delete characters (scroll left) */
            case 'P':

                amount = argv[0];
                if (amount == 0) amount = 1;

                /* Scroll left by amount */
                if (term->cursor_col + amount < term->term_width)
                    guac_terminal_copy_columns(term, term->cursor_row,
                            term->cursor_col + amount, term->term_width - 1,
                            -amount);

                /* Clear right */
                guac_terminal_clear_columns(term, term->cursor_row,
                        term->term_width - amount, term->term_width - 1);

                break;

            /* S: Scroll Up by amount */
            case 'S':

                /* Get move amount */
                amount = argv[0];
                if (amount == 0) amount = 1;

                /* Scroll up */
                guac_terminal_scroll_up(term, term->scroll_start,
                        term->scroll_end, amount);

                break;

            /* T: Scroll Down by amount */
            case 'T':

                /* Get move amount */
                amount = argv[0];
                if (amount == 0) amount = 1;

                /* Scroll Down */
                guac_terminal_scroll_down(term, term->scroll_start,
                        term->scroll_end, amount);

                break;

            /* X: Erase characters (no scroll) */
            case 'X':

                amount = argv[0];
                if (amount == 0) amount = 1;

                /* Clear characters */
                guac_terminal_clear_columns(term, term->cursor_row,
                        term->cursor_col, term->cursor_col + amount - 1);

                break;

            /* ]: Linux Private CSI */
            case ']':
                /* Explicitly ignored */
                break;

            /* c: Identify */
            case 'c':
                if (argv[0] == 0 && private_mode_character == 0)
                    guac_terminal_send_string(term, GUAC_TERMINAL_VT102_ID);
                break;

            /* d: Move cursor, current col */
            case 'd':
                row = argv[0]; if (row != 0) row--;
                guac_terminal_move_cursor(term, row, term->cursor_col);
                break;

            /* g: Clear tab */
            case 'g':

                /* Clear tab at current location */
                if (argv[0] == 0)
                    guac_terminal_unset_tab(term, term->cursor_col);

                /* Clear all tabs */
                else if (argv[0] == 3)
                    guac_terminal_clear_tabs(term);

                break;

            /* h: Set Mode (DECSET) */
            case 'h':

                /* Save cursor for later restoration */
                if (argv[0] == GUAC_TERMINAL_DECSET_SAVE_CURSOR
                        || argv[0] == GUAC_TERMINAL_DECSET_USE_ALT_BUFFER_AND_SAVE_CURSOR_AND_CLEAR) {
                    term->saved_cursor_row = term->cursor_row;
                    term->saved_cursor_col = term->cursor_col;
                }

                if (term->current_buffer != term->alternate_buffer) {

                    /* Switch to alternate buffer */
                    if (argv[0] == GUAC_TERMINAL_DECSET_USE_ALT_BUFFER
                            || argv[0] == GUAC_TERMINAL_DECSET_USE_ALT_BUFFER_AND_CLEAR
                            || argv[0] == GUAC_TERMINAL_DECSET_USE_ALT_BUFFER_AND_SAVE_CURSOR_AND_CLEAR) {

                        term->current_buffer = term->alternate_buffer;

                        /* Update scrollbar bounds (the different buffers have differing levels of scrollback) */
                        guac_terminal_scrollbar_set_bounds(term->scrollbar,
                                -guac_terminal_get_available_scroll(term), 0);

                    }

                    /* Clear alternate buffer only if we were previously using
                     * the normal buffer */
                    if (argv[0] == GUAC_TERMINAL_DECSET_USE_ALT_BUFFER_AND_CLEAR
                            || argv[0] == GUAC_TERMINAL_DECSET_USE_ALT_BUFFER_AND_SAVE_CURSOR_AND_CLEAR) {
                        guac_terminal_clear_range(term,
                                0, 0, term->term_height - 1, term->term_width - 1);
                    }

                }

                /* Look up flag and set */
                flag = __guac_terminal_get_flag(term, argv[0], private_mode_character);
                if (flag != NULL)
                    *flag = true;

                break;

            /* l: Reset Mode (DECRST) */
            case 'l':

                if (term->current_buffer != term->normal_buffer) {

                    /* Switch back to normal buffer */
                    if (argv[0] == GUAC_TERMINAL_DECRST_USE_NORMAL_BUFFER
                            || argv[0] == GUAC_TERMINAL_DECRST_USE_NORMAL_BUFFER_AND_CLEAR
                            || argv[0] == GUAC_TERMINAL_DECRST_USE_NORMAL_BUFFER_AND_RESTORE_CURSOR) {

                        term->current_buffer = term->normal_buffer;

                        /* Update scrollbar bounds (the different buffers have differing levels of scrollback) */
                        guac_terminal_scrollbar_set_bounds(term->scrollbar,
                                -guac_terminal_get_available_scroll(term), 0);

                        /* Redraw normal buffer content */
                        guac_terminal_redraw_default_layer(term);
                        
                        /* Clear selection */
                        term->text_selected = false;
                        term->selection_committed = false;

                    }

                    /* Clear normal buffer only if we were previously using
                     * the alternate buffer */
                    if (argv[0] == GUAC_TERMINAL_DECRST_USE_NORMAL_BUFFER_AND_CLEAR) {
                        guac_terminal_clear_range(term,
                                0, 0, term->term_height - 1, term->term_width - 1);
                    }

                }

                /* Restore previously saved cursor */
                if (argv[0] == GUAC_TERMINAL_DECRST_RESTORE_CURSOR
                        || argv[0] == GUAC_TERMINAL_DECRST_USE_NORMAL_BUFFER_AND_RESTORE_CURSOR) {
                    guac_terminal_move_cursor(term,
                            term->saved_cursor_row,
                            term->saved_cursor_col);
                }

                /* Look up flag and clear */
                flag = __guac_terminal_get_flag(term, argv[0], private_mode_character);
                if (flag != NULL)
                    *flag = false;

                break;

            /* m: Set graphics rendition */
            case 'm':

                for (i=0; i<argc; i++) {

                    int value = argv[i];

                    /* Reset attributes */
                    if (value == 0)
                        term->current_attributes = term->default_char.attributes;

                    /* Bold */
                    else if (value == 1)
                        term->current_attributes.bold = true;

                    /* Faint (low intensity) */
                    else if (value == 2)
                        term->current_attributes.half_bright = true;

                    /* Underscore on */
                    else if (value == 4)
                        term->current_attributes.underscore = true;

                    /* Reverse video */
                    else if (value == 7)
                        term->current_attributes.reverse = true;

                    /* Normal intensity (not bold) */
                    else if (value == 21 || value == 22) {
                        term->current_attributes.bold = false;
                        term->current_attributes.half_bright = false;
                    }

                    /* Reset underscore */
                    else if (value == 24)
                        term->current_attributes.underscore = false;

                    /* Reset reverse video */
                    else if (value == 27)
                        term->current_attributes.reverse = false;

                    /* Foreground */
                    else if (value >= 30 && value <= 37)
                        guac_terminal_display_lookup_color(term->display,
                                value - 30,
                                &term->current_attributes.foreground);

                    /* Underscore on, default foreground OR 256-color
                     * foreground */
                    else if (value == 38) {

                        /* Attempt to set foreground with 256-color entry */
                        int xterm256_length =
                            guac_terminal_parse_xterm256(term,
                                    argc - i - 1, &argv[i + 1],
                                    &term->current_attributes.foreground);

                        /* If valid 256-color entry, foreground has been set */
                        if (xterm256_length > 0)
                            i += xterm256_length;

                        /* Otherwise interpret as underscore and default
                         * foreground  */
                        else {
                            term->current_attributes.underscore = true;
                            term->current_attributes.foreground =
                                term->default_char.attributes.foreground;
                        }

                    }

                    /* Underscore off, default foreground */
                    else if (value == 39) {
                        term->current_attributes.underscore = false;
                        term->current_attributes.foreground =
                            term->default_char.attributes.foreground;
                    }

                    /* Background */
                    else if (value >= 40 && value <= 47)
                        guac_terminal_display_lookup_color(term->display,
                                value - 40,
                                &term->current_attributes.background);

                    /* 256-color background */
                    else if (value == 48)
                        i += guac_terminal_parse_xterm256(term,
                                argc - i - 1, &argv[i + 1],
                                &term->current_attributes.background);

                    /* Reset background */
                    else if (value == 49)
                        term->current_attributes.background =
                            term->default_char.attributes.background;

                    /* Intense foreground */
                    else if (value >= 90 && value <= 97)
                        guac_terminal_display_lookup_color(term->display,
                                value - 90 + GUAC_TERMINAL_FIRST_INTENSE,
                                &term->current_attributes.foreground);

                    /* Intense background */
                    else if (value >= 100 && value <= 107)
                        guac_terminal_display_lookup_color(term->display,
                                value - 100 + GUAC_TERMINAL_FIRST_INTENSE,
                                &term->current_attributes.background);

                }

                break;

            /* n: Status report */
            case 'n':

                /* Device status report */
                if (argv[0] == 5 && private_mode_character == 0)
                    guac_terminal_send_string(term, GUAC_TERMINAL_OK);

                /* Cursor position report */
                else if (argv[0] == 6 && private_mode_character == 0)
                    guac_terminal_sendf(term, "\x1B[%i;%iR", term->cursor_row+1, term->cursor_col+1);

                break;

            /* q: Set keyboard LEDs */
            case 'q':
                /* Explicitly ignored */
                break;

            /* r: Set scrolling region */
            case 'r':

                /* If parameters given, set region */
                if (argc == 2) {
                    term->scroll_start = argv[0]-1;
                    term->scroll_end   = argv[1]-1;
                }

                /* Otherwise, reset scrolling region */
                else {
                    term->scroll_start = 0;
                    term->scroll_end = term->term_height - 1;
                }

                break;

            /* Save Cursor */
            case 's':
                term->saved_cursor_row = term->cursor_row;
                term->saved_cursor_col = term->cursor_col;
                break;

            /* Restore Cursor */
            case 'u':
                guac_terminal_move_cursor(term,
                        term->saved_cursor_row,
                        term->saved_cursor_col);
                break;

            /* Warn of unhandled codes */
            default:
                if (c != ';') {

                    guac_client_log(term->client, GUAC_LOG_DEBUG,
                            "Unhandled CSI sequence: %c", c);

                    for (i=0; i<argc; i++)
                        guac_client_log(term->client, GUAC_LOG_DEBUG,
                                " -> argv[%i] = %i", i, argv[i]);

                }

        }

        /* If not a semicolon, end of CSI sequence */
        if (c != ';') {
            term->char_handler = guac_terminal_echo;

            /* Reset parameters */
            for (i=0; i<argc; i++)
                argv[i] = 0;

            /* Reset private mode character */
            private_mode_character = 0;

            /* Reset argument counters */
            argc = 0;
            argv_length = 0;
        }

    }

    /* Set private mode character if given and unset */
    else if (c >= 0x3A && c <= 0x3F && private_mode_character == 0)
        private_mode_character = c;

    return 0;

}