X-Git-Url: http://git.madism.org/?a=blobdiff_plain;f=madtty%2Finject.c;h=b60b6c59dfb60a003d9c34e7958faf3a1dcb4625;hb=5b8c62d790da08fe9b6e32b6b9dda5ae25d5b2d8;hp=d4b05da0f09dd6710b098fbc6d76e21e7f29db01;hpb=fcbd5f6835f65055d0ac14d3a2da3afb39634fd3;p=apps%2Fmadtty.git diff --git a/madtty/inject.c b/madtty/inject.c index d4b05da..b60b6c5 100644 --- a/madtty/inject.c +++ b/madtty/inject.c @@ -24,253 +24,629 @@ #include "madtty.h" #include "roteprivate.h" -#include "inject_csi.h" - -static void cursor_line_down(RoteTerm *rt) { - int i; - rt->crow++; - rt->curpos_dirty = true; - if (rt->crow <= rt->pd->scrollbottom) return; - - /* must scroll the scrolling region up by 1 line, and put cursor on - * last line of it */ - rt->crow = rt->pd->scrollbottom; - - for (i = rt->pd->scrolltop; i < rt->pd->scrollbottom; i++) { - rt->line_dirty[i] = true; - memcpy(rt->cells[i], rt->cells[i+1], sizeof(RoteCell) * rt->cols); - } - - rt->line_dirty[rt->pd->scrollbottom] = true; - - /* clear last row of the scrolling region */ - for (i = 0; i < rt->cols; i++) { - rt->cells[rt->pd->scrollbottom][i].ch = 0x20; - rt->cells[rt->pd->scrollbottom][i].attr = 0x70; - } + +#define MAX_CSI_ES_PARAMS 32 + +static inline void clamp_cursor_to_bounds(RoteTerm *rt) +{ + if (rt->crow < 0) rt->curpos_dirty = true, rt->crow = 0; + if (rt->ccol < 0) rt->curpos_dirty = true, rt->ccol = 0; + + if (rt->crow >= rt->rows) + rt->curpos_dirty = true, rt->crow = rt->rows - 1; + + if (rt->ccol >= rt->cols) + rt->curpos_dirty = true, rt->ccol = rt->cols - 1; +} + +static void cursor_line_down(RoteTerm *rt) +{ + int i; + rt->crow++; + rt->curpos_dirty = true; + if (rt->crow <= rt->pd->scrollbottom) return; + + /* must scroll the scrolling region up by 1 line, and put cursor on + * last line of it */ + rt->crow = rt->pd->scrollbottom; + + for (i = rt->pd->scrolltop; i < rt->pd->scrollbottom; i++) { + rt->line_dirty[i] = true; + memcpy(rt->cells[i], rt->cells[i+1], sizeof(RoteCell) * rt->cols); + } + + rt->line_dirty[rt->pd->scrollbottom] = true; + + /* clear last row of the scrolling region */ + for (i = 0; i < rt->cols; i++) { + rt->cells[rt->pd->scrollbottom][i].ch = 0x20; + rt->cells[rt->pd->scrollbottom][i].attr = 0x70; + } } -static void cursor_line_up(RoteTerm *rt) { - int i; - rt->crow--; - rt->curpos_dirty = true; - if (rt->crow >= rt->pd->scrolltop) return; - - /* must scroll the scrolling region up by 1 line, and put cursor on - * first line of it */ - rt->crow = rt->pd->scrolltop; - - for (i = rt->pd->scrollbottom; i > rt->pd->scrolltop; i--) { - rt->line_dirty[i] = true; - memcpy(rt->cells[i], rt->cells[i-1], sizeof(RoteCell) * rt->cols); - } - - rt->line_dirty[rt->pd->scrolltop] = true; - - /* clear first row of the scrolling region */ - for (i = 0; i < rt->cols; i++) { - rt->cells[rt->pd->scrolltop][i].ch = 0x20; - rt->cells[rt->pd->scrolltop][i].attr = 0x70; - } +static void cursor_line_up(RoteTerm *rt) +{ + int i; + rt->crow--; + rt->curpos_dirty = true; + if (rt->crow >= rt->pd->scrolltop) return; + + /* must scroll the scrolling region up by 1 line, and put cursor on + * first line of it */ + rt->crow = rt->pd->scrolltop; + + for (i = rt->pd->scrollbottom; i > rt->pd->scrolltop; i--) { + rt->line_dirty[i] = true; + memcpy(rt->cells[i], rt->cells[i-1], sizeof(RoteCell) * rt->cols); + } + + rt->line_dirty[rt->pd->scrolltop] = true; + + /* clear first row of the scrolling region */ + for (i = 0; i < rt->cols; i++) { + rt->cells[rt->pd->scrolltop][i].ch = 0x20; + rt->cells[rt->pd->scrolltop][i].attr = 0x70; + } } -static inline void put_normal_char(RoteTerm *rt, char c) { - if (rt->ccol >= rt->cols) { - rt->ccol = 0; - cursor_line_down(rt); - } +static inline void put_normal_char(RoteTerm *rt, char c) +{ + if (rt->ccol >= rt->cols) { + rt->ccol = 0; + cursor_line_down(rt); + } - if (rt->insert) { - int i; + if (rt->insert) { + int i; - for(i = rt->cols - 1; i >= rt->ccol+1; i--) - rt->cells[rt->crow][i] = rt->cells[rt->crow][i-1]; - } + for(i = rt->cols - 1; i >= rt->ccol+1; i--) + rt->cells[rt->crow][i] = rt->cells[rt->crow][i-1]; + } - rt->cells[rt->crow][rt->ccol].ch = c; - rt->cells[rt->crow][rt->ccol].attr = rt->curattr; - rt->ccol++; + rt->cells[rt->crow][rt->ccol].ch = c; + rt->cells[rt->crow][rt->ccol].attr = rt->curattr; + rt->ccol++; - rt->line_dirty[rt->crow] = true; - rt->curpos_dirty = true; + rt->line_dirty[rt->crow] = true; + rt->curpos_dirty = true; } -static inline void put_graphmode_char(RoteTerm *rt, char c) { - char nc; - /* do some very pitiful translation to regular ascii chars */ - switch (c) { +static inline void put_graphmode_char(RoteTerm *rt, char c) +{ + char nc; + /* do some very pitiful translation to regular ascii chars */ + switch (c) { case 'j': case 'k': case 'l': case 'm': case 'n': case 't': - case 'u': case 'v': case 'w': - nc = '+'; break; + case 'u': case 'v': case 'w': + nc = '+'; break; case 'x': - nc = '|'; break; + nc = '|'; break; default: - nc = '%'; - } + nc = '%'; + } - put_normal_char(rt, nc); + put_normal_char(rt, nc); } -static inline void new_escape_sequence(RoteTerm *rt) { - rt->pd->escaped = true; - rt->pd->esbuf_len = 0; - rt->pd->esbuf[0] = '\0'; +static inline void new_escape_sequence(RoteTerm *rt) +{ + rt->pd->escaped = true; + rt->pd->esbuf_len = 0; + rt->pd->esbuf[0] = '\0'; } -static inline void cancel_escape_sequence(RoteTerm *rt) { - rt->pd->escaped = false; - rt->pd->esbuf_len = 0; - rt->pd->esbuf[0] = '\0'; +static inline void cancel_escape_sequence(RoteTerm *rt) +{ + rt->pd->escaped = false; + rt->pd->esbuf_len = 0; + rt->pd->esbuf[0] = '\0'; } -static void handle_control_char(RoteTerm *rt, char c) { - switch (c) { +static void handle_control_char(RoteTerm *rt, char c) +{ + switch (c) { case '\r': rt->ccol = 0; break; /* carriage return */ case '\n': /* line feed */ - rt->ccol = 0; cursor_line_down(rt); - rt->curpos_dirty = true; - break; + rt->ccol = 0; cursor_line_down(rt); + rt->curpos_dirty = true; + break; case '\b': /* backspace */ - if (rt->ccol > 0) rt->ccol--; - rt->curpos_dirty = true; - break; + if (rt->ccol > 0) rt->ccol--; + rt->curpos_dirty = true; + break; case '\t': /* tab */ - rt->ccol += 8 - (rt->ccol % 8); - clamp_cursor_to_bounds(rt); - break; + rt->ccol += 8 - (rt->ccol % 8); + clamp_cursor_to_bounds(rt); + break; case '\x1B': /* begin escape sequence (aborting previous one if any) */ - new_escape_sequence(rt); - break; + new_escape_sequence(rt); + break; case '\x0E': /* enter graphical character mode */ - rt->pd->graphmode = true; - break; + rt->pd->graphmode = true; + break; case '\x0F': /* exit graphical character mode */ - rt->pd->graphmode = false; - break; + rt->pd->graphmode = false; + break; case '\x9B': /* CSI character. Equivalent to ESC [ */ - new_escape_sequence(rt); - rt->pd->esbuf[rt->pd->esbuf_len++] = '['; - break; + new_escape_sequence(rt); + rt->pd->esbuf[rt->pd->esbuf_len++] = '['; + break; case '\x18': case '\x1A': /* these interrupt escape sequences */ - cancel_escape_sequence(rt); - break; + cancel_escape_sequence(rt); + break; case '\a': /* bell */ - /* do nothing for now... maybe a visual bell would be nice? */ - break; - #ifdef DEBUG + /* do nothing for now... maybe a visual bell would be nice? */ + break; +#ifdef DEBUG default: - fprintf(stderr, "Unrecognized control char: %d (^%c)\n", c, c + '@'); - break; - #endif - } + fprintf(stderr, "Unrecognized control char: %d (^%c)\n", c, c + '@'); + break; +#endif + } } -static inline bool is_valid_csi_ender(char c) { - return (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - c == '@' || c == '`'; +static inline bool is_valid_csi_ender(char c) +{ + return (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + c == '@' || c == '`'; } -static void try_interpret_escape_seq(RoteTerm *rt) { - char firstchar = rt->pd->esbuf[0]; - char lastchar = rt->pd->esbuf[rt->pd->esbuf_len-1]; - - if (!firstchar) return; /* too early to do anything */ - - if (rt->pd->handler) { - /* call custom handler */ - #ifdef DEBUG - fprintf(stderr, "Calling custom handler for ES <%s>.\n", rt->pd->esbuf); - #endif - - int answer = (*(rt->pd->handler))(rt, rt->pd->esbuf); - if (answer == ROTE_HANDLERESULT_OK) { - /* successfully handled */ - #ifdef DEBUG - fprintf(stderr, "Handler returned OK. Done with escape sequence.\n"); - #endif - - cancel_escape_sequence(rt); - return; - } - else if (answer == ROTE_HANDLERESULT_NOTYET) { - /* handler might handle it when more characters are appended to - * it. So for now we don't interpret it */ - #ifdef DEBUG - fprintf(stderr, "Handler returned NOTYET. Waiting for more chars.\n"); - #endif - - return; - } - - /* If we got here then answer == ROTE_HANDLERESULT_NOWAY */ - /* handler said it can't handle that escape sequence, - * but we can still try handling it ourselves, so - * we proceed normally. */ - #ifdef DEBUG - fprintf(stderr, "Handler returned NOWAY. Trying our handlers.\n"); - #endif - } - - /* interpret ESC-M as reverse line-feed */ - if (firstchar == 'M') { - cursor_line_up(rt); - cancel_escape_sequence(rt); - return; - } - - if (firstchar != '[' && firstchar != ']') { - /* unrecognized escape sequence. Let's forget about it. */ - #ifdef DEBUG - fprintf(stderr, "Unrecognized ES: <%s>\n", rt->pd->esbuf); - #endif - - cancel_escape_sequence(rt); - return; - } - - if (firstchar == '[' && is_valid_csi_ender(lastchar)) { - /* we have a csi escape sequence: interpret it */ - rote_es_interpret_csi(rt); - cancel_escape_sequence(rt); - } - else if (firstchar == ']' && lastchar == '\a') { - /* we have an xterm escape sequence: interpret it */ - - /* rote_es_interpret_xterm_es(rt); -- TODO!*/ - #ifdef DEBUG - fprintf(stderr, "Ignored XTerm ES.\n"); - #endif - cancel_escape_sequence(rt); - } - - /* if the escape sequence took up all available space and could - * not yet be parsed, abort it */ - if (rt->pd->esbuf_len + 1 >= ESEQ_BUF_SIZE) cancel_escape_sequence(rt); +static void try_interpret_escape_seq(RoteTerm *rt) +{ + char firstchar = rt->pd->esbuf[0]; + char lastchar = rt->pd->esbuf[rt->pd->esbuf_len-1]; + + if (!firstchar) return; /* too early to do anything */ + + if (rt->pd->handler) { + /* call custom handler */ +#ifdef DEBUG + fprintf(stderr, "Calling custom handler for ES <%s>.\n", rt->pd->esbuf); +#endif + + int answer = (*(rt->pd->handler))(rt, rt->pd->esbuf); + if (answer == ROTE_HANDLERESULT_OK) { + /* successfully handled */ +#ifdef DEBUG + fprintf(stderr, "Handler returned OK. Done with escape sequence.\n"); +#endif + + cancel_escape_sequence(rt); + return; + } + else if (answer == ROTE_HANDLERESULT_NOTYET) { + /* handler might handle it when more characters are appended to + * it. So for now we don't interpret it */ +#ifdef DEBUG + fprintf(stderr, "Handler returned NOTYET. Waiting for more chars.\n"); +#endif + + return; + } + + /* If we got here then answer == ROTE_HANDLERESULT_NOWAY */ + /* handler said it can't handle that escape sequence, + * but we can still try handling it ourselves, so + * we proceed normally. */ +#ifdef DEBUG + fprintf(stderr, "Handler returned NOWAY. Trying our handlers.\n"); +#endif + } + + /* interpret ESC-M as reverse line-feed */ + if (firstchar == 'M') { + cursor_line_up(rt); + cancel_escape_sequence(rt); + return; + } + + if (firstchar != '[' && firstchar != ']') { + /* unrecognized escape sequence. Let's forget about it. */ +#ifdef DEBUG + fprintf(stderr, "Unrecognized ES: <%s>\n", rt->pd->esbuf); +#endif + + cancel_escape_sequence(rt); + return; + } + + if (firstchar == '[' && is_valid_csi_ender(lastchar)) { + /* we have a csi escape sequence: interpret it */ + rote_es_interpret_csi(rt); + cancel_escape_sequence(rt); + } + else if (firstchar == ']' && lastchar == '\a') { + /* we have an xterm escape sequence: interpret it */ + + /* rote_es_interpret_xterm_es(rt); -- TODO!*/ +#ifdef DEBUG + fprintf(stderr, "Ignored XTerm ES.\n"); +#endif + cancel_escape_sequence(rt); + } + + /* if the escape sequence took up all available space and could + * not yet be parsed, abort it */ + if (rt->pd->esbuf_len + 1 >= ESEQ_BUF_SIZE) cancel_escape_sequence(rt); } - -void rote_vt_inject(RoteTerm *rt, const char *data, int len) { - int i; - for (i = 0; i < len; i++, data++) { - if (*data == 0) continue; /* completely ignore NUL */ - if (*data >= 1 && *data <= 31) { - handle_control_char(rt, *data); - continue; - } - - if (rt->pd->escaped && rt->pd->esbuf_len < ESEQ_BUF_SIZE) { - /* append character to ongoing escape sequence */ - rt->pd->esbuf[rt->pd->esbuf_len] = *data; - rt->pd->esbuf[++rt->pd->esbuf_len] = 0; - - try_interpret_escape_seq(rt); - } - else if (rt->pd->graphmode) - put_graphmode_char(rt, *data); - else - put_normal_char(rt, *data); - } + +void rote_vt_inject(RoteTerm *rt, const char *data, int len) +{ + int i; + for (i = 0; i < len; i++, data++) { + if (*data == 0) continue; /* completely ignore NUL */ + if (*data >= 1 && *data <= 31) { + handle_control_char(rt, *data); + continue; + } + + if (rt->pd->escaped && rt->pd->esbuf_len < ESEQ_BUF_SIZE) { + /* append character to ongoing escape sequence */ + rt->pd->esbuf[rt->pd->esbuf_len] = *data; + rt->pd->esbuf[++rt->pd->esbuf_len] = 0; + + try_interpret_escape_seq(rt); + } + else if (rt->pd->graphmode) + put_graphmode_char(rt, *data); + else + put_normal_char(rt, *data); + } +} + +/****************************************************************************/ +/* CSI things */ +/****************************************************************************/ + +/* interprets a 'set attribute' (SGR) CSI escape sequence */ +static void interpret_csi_SGR(RoteTerm *rt, int param[], int pcount) +{ + int i; + + if (pcount == 0) { + /* special case: reset attributes */ + rt->curattr = 0x70; + return; + } + + for (i = 0; i < pcount; i++) { + + // From http://vt100.net/docs/vt510-rm/SGR table 5-16 + // 0 All attributes off + // 1 Bold + // 4 Underline + // 5 Blinking + // 7 Negative image + // 8 Invisible image + // 10 The ASCII character set is the current 7-bit + // display character set (default) - SCO Console only. + // 11 Map Hex 00-7F of the PC character set codes + // to the current 7-bit display character set + // - SCO Console only. + // 12 Map Hex 80-FF of the current character set to + // the current 7-bit display character set - SCO + // Console only. + // 22 Bold off + // 24 Underline off + // 25 Blinking off + // 27 Negative image off + // 28 Invisible image off + + if (param[i] == 0) rt->curattr = 0x70; + else if (param[i] == 1 || param[i] == 2 || param[i] == 4) /* set bold */ + ROTE_ATTR_MOD_BOLD(rt->curattr,1); + else if (param[i] == 5) /* set blink */ + ROTE_ATTR_MOD_BLINK(rt->curattr,1); + else if (param[i] == 7 || param[i] == 27) { /* reverse video */ + int fg = ROTE_ATTR_FG(rt->curattr); + int bg = ROTE_ATTR_BG(rt->curattr); + ROTE_ATTR_MOD_FG(rt->curattr, bg); + ROTE_ATTR_MOD_BG(rt->curattr, fg); + } + else if (param[i] == 8) rt->curattr = 0x0; /* invisible */ + else if (param[i] == 22 || param[i] == 24) /* bold off */ + ROTE_ATTR_MOD_BOLD(rt->curattr,0); + else if (param[i] == 25) /* blink off */ + ROTE_ATTR_MOD_BLINK(rt->curattr,0); + else if (param[i] == 28) /* invisible off */ + rt->curattr = 0x70; + else if (param[i] >= 30 && param[i] <= 37) /* set fg */ + ROTE_ATTR_MOD_FG(rt->curattr, param[i] - 30); + else if (param[i] >= 40 && param[i] <= 47) /* set bg */ + ROTE_ATTR_MOD_BG(rt->curattr, param[i] - 40); + else if (param[i] == 39) /* reset foreground to default */ + ROTE_ATTR_MOD_FG(rt->curattr, 7); + else if (param[i] == 49) /* reset background to default */ + ROTE_ATTR_MOD_BG(rt->curattr, 0); + } +} + +/* interprets an 'erase display' (ED) escape sequence */ +static void interpret_csi_ED(RoteTerm *rt, int param[], int pcount) +{ + int r, c; + int start_row, start_col, end_row, end_col; + + /* decide range */ + if (pcount && param[0] == 2) + start_row = 0, start_col = 0, end_row = rt->rows - 1, + end_col = rt->cols - 1; + + else if (pcount && param[0] == 1) + start_row = 0, start_col = 0, end_row = rt->crow, + end_col = rt->ccol; + + else start_row = rt->crow, start_col = rt->ccol, + end_row = rt->rows - 1, end_col = rt->cols - 1; + + /* clean range */ + for (r = start_row; r <= end_row; r++) { + rt->line_dirty[r] = true; + + for (c = (r == start_row ? start_col : 0); + c <= (r == end_row ? end_col : rt->cols - 1); + c++) { + rt->cells[r][c].ch = 0x20; + rt->cells[r][c].attr = rt->curattr; + } + } +} + +/* interprets a 'move cursor' (CUP) escape sequence */ +static void interpret_csi_CUP(RoteTerm *rt, int param[], int pcount) +{ + if (pcount == 0) { + /* special case */ + rt->crow = rt->ccol = 0; + return; + } + else if (pcount < 2) return; /* malformed */ + + rt->crow = param[0] - 1; /* convert from 1-based to 0-based */ + rt->ccol = param[1] - 1; /* convert from 1-based to 0-based */ + + rt->curpos_dirty = true; + + clamp_cursor_to_bounds(rt); +} + +/* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL, + * CPL, CHA, HPR, VPA, VPR, HPA */ +static void interpret_csi_C(RoteTerm *rt, char verb, + int param[], int pcount) { + int n = (pcount && param[0] > 0) ? param[0] : 1; + + switch (verb) { + case 'A': rt->crow -= n; break; + case 'B': case 'e': rt->crow += n; break; + case 'C': case 'a': rt->ccol += n; break; + case 'D': rt->ccol -= n; break; + case 'E': rt->crow += n; rt->ccol = 0; break; + case 'F': rt->crow -= n; rt->ccol = 0; break; + case 'G': case '`': rt->ccol = param[0] - 1; break; + case 'd': rt->crow = param[0] - 1; break; + } + + rt->curpos_dirty = true; + clamp_cursor_to_bounds(rt); +} + +/* Interpret the 'erase line' escape sequence */ +static void interpret_csi_EL(RoteTerm *rt, int param[], int pcount) +{ + int erase_start, erase_end, i; + int cmd = pcount ? param[0] : 0; + + switch (cmd) { + case 1: erase_start = 0; erase_end = rt->ccol; break; + case 2: erase_start = 0; erase_end = rt->cols - 1; break; + default: erase_start = rt->ccol; erase_end = rt->cols - 1; break; + } + + for (i = erase_start; i <= erase_end; i++) { + rt->cells[rt->crow][i].ch = 0x20; + rt->cells[rt->crow][i].attr = rt->curattr; + } + + rt->line_dirty[rt->crow] = true; +} + +/* Interpret the 'insert blanks' sequence (ICH) */ +static void interpret_csi_ICH(RoteTerm *rt, int param[], int pcount) +{ + int n = (pcount && param[0] > 0) ? param[0] : 1; + int i; + for (i = rt->cols - 1; i >= rt->ccol + n; i--) + rt->cells[rt->crow][i] = rt->cells[rt->crow][i - n]; + for (i = rt->ccol; i < rt->ccol + n; i++) { + rt->cells[rt->crow][i].ch = 0x20; + rt->cells[rt->crow][i].attr = rt->curattr; + } + + rt->line_dirty[rt->crow] = true; +} + +/* Interpret the 'delete chars' sequence (DCH) */ +static void interpret_csi_DCH(RoteTerm *rt, int param[], int pcount) +{ + int n = (pcount && param[0] > 0) ? param[0] : 1; + int i; + for (i = rt->ccol; i < rt->cols; i++) { + if (i + n < rt->cols) + rt->cells[rt->crow][i] = rt->cells[rt->crow][i + n]; + else { + rt->cells[rt->crow][i].ch = 0x20; + rt->cells[rt->crow][i].attr = rt->curattr; + } + } + + rt->line_dirty[rt->crow] = true; +} + +/* Interpret an 'insert line' sequence (IL) */ +static void interpret_csi_IL(RoteTerm *rt, int param[], int pcount) +{ + int n = (pcount && param[0] > 0) ? param[0] : 1; + int i, j; + + for (i = rt->pd->scrollbottom; i >= rt->crow + n; i--) + memcpy(rt->cells[i], rt->cells[i - n], sizeof(RoteCell) * rt->cols); + + for (i = rt->crow; i < rt->crow + n && i <= rt->pd->scrollbottom; i++) { + rt->line_dirty[i] = true; + for (j = 0; j < rt->cols; j++) + rt->cells[i][j].ch = 0x20, rt->cells[i][j].attr = rt->curattr; + } + +} + +/* Interpret a 'delete line' sequence (DL) */ +static void interpret_csi_DL(RoteTerm *rt, int param[], int pcount) +{ + int n = (pcount && param[0] > 0) ? param[0] : 1; + int i, j; + + for (i = rt->crow; i <= rt->pd->scrollbottom; i++) { + rt->line_dirty[i] = true; + if (i + n <= rt->pd->scrollbottom) + memcpy(rt->cells[i], rt->cells[i+n], sizeof(RoteCell) * rt->cols); + else { + for (j = 0; j < rt->cols; j++) + rt->cells[i][j].ch = 0x20, rt->cells[i][j].attr = rt->curattr; + } + } +} + +/* Interpret an 'erase characters' (ECH) sequence */ +static void interpret_csi_ECH(RoteTerm *rt, int param[], int pcount) +{ + int n = (pcount && param[0] > 0) ? param[0] : 1; + int i; + + for (i = rt->ccol; i < rt->ccol + n && i < rt->cols; i++) { + rt->cells[rt->crow][i].ch = 0x20; + rt->cells[rt->crow][i].attr = rt->curattr; + } + + rt->line_dirty[rt->crow] = true; +} + +/* Interpret a 'set scrolling region' (DECSTBM) sequence */ +static void interpret_csi_DECSTBM(RoteTerm *rt, int param[], int pcount) +{ + int newtop, newbottom; + + if (!pcount) { + newtop = 0; + newbottom = rt->rows - 1; + } + else if (pcount < 2) return; /* malformed */ + else { + newtop = param[0] - 1; + newbottom = param[1] - 1; + } + + /* clamp to bounds */ + if (newtop < 0) newtop = 0; + if (newtop >= rt->rows) newtop = rt->rows - 1; + if (newbottom < 0) newbottom = 0; + if (newbottom >= rt->rows) newbottom = rt->rows - 1; + + /* check for range validity */ + if (newtop > newbottom) return; + rt->pd->scrolltop = newtop; + rt->pd->scrollbottom = newbottom; +} + +static void interpret_csi_SAVECUR(RoteTerm *rt, int param[], int pcount) +{ + rt->pd->saved_x = rt->ccol; + rt->pd->saved_y = rt->crow; +} + +static void interpret_csi_RESTORECUR(RoteTerm *rt, int param[], int pcount) +{ + rt->ccol = rt->pd->saved_x; + rt->crow = rt->pd->saved_y; + rt->curpos_dirty = true; +} + +void rote_es_interpret_csi(RoteTerm *rt) +{ + static int csiparam[MAX_CSI_ES_PARAMS]; + int param_count = 0; + const char *p = rt->pd->esbuf + 1; + char verb = rt->pd->esbuf[rt->pd->esbuf_len - 1]; + + if (!strncmp(rt->pd->esbuf, "[?", 2)) { /* private-mode CSI, ignore */ +#ifdef DEBUG + fprintf(stderr, "Ignoring private-mode CSI: <%s>\n", rt->pd->esbuf); +#endif + return; + } + + /* parse numeric parameters */ + while ((*p >= '0' && *p <= '9') || *p == ';') { + if (*p == ';') { + if (param_count >= MAX_CSI_ES_PARAMS) return; /* too long! */ + csiparam[param_count++] = 0; + } + else { + if (param_count == 0) csiparam[param_count++] = 0; + csiparam[param_count - 1] *= 10; + csiparam[param_count - 1] += *p - '0'; + } + + p++; + } + + /* delegate handling depending on command character (verb) */ + switch (verb) { + case 'h': + if (param_count == 1 && csiparam[0] == 4) /* insert mode */ + rt->insert = true; + break; + case 'l': + if (param_count == 1 && csiparam[0] == 4) /* replace mode */ + rt->insert = false; + break; + case 'm': /* it's a 'set attribute' sequence */ + interpret_csi_SGR(rt, csiparam, param_count); break; + case 'J': /* it's an 'erase display' sequence */ + interpret_csi_ED(rt, csiparam, param_count); break; + case 'H': case 'f': /* it's a 'move cursor' sequence */ + interpret_csi_CUP(rt, csiparam, param_count); break; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + case 'e': case 'a': case 'd': case '`': + /* it is a 'relative move' */ + interpret_csi_C(rt, verb, csiparam, param_count); break; + case 'K': /* erase line */ + interpret_csi_EL(rt, csiparam, param_count); break; + case '@': /* insert characters */ + interpret_csi_ICH(rt, csiparam, param_count); break; + case 'P': /* delete characters */ + interpret_csi_DCH(rt, csiparam, param_count); break; + case 'L': /* insert lines */ + interpret_csi_IL(rt, csiparam, param_count); break; + case 'M': /* delete lines */ + interpret_csi_DL(rt, csiparam, param_count); break; + case 'X': /* erase chars */ + interpret_csi_ECH(rt, csiparam, param_count); break; + case 'r': /* set scrolling region */ + interpret_csi_DECSTBM(rt, csiparam, param_count); break; + case 's': /* save cursor location */ + interpret_csi_SAVECUR(rt, csiparam, param_count); break; + case 'u': /* restore cursor location */ + interpret_csi_RESTORECUR(rt, csiparam, param_count); break; + default: +#ifdef DEBUG + fprintf(stderr, "Unrecogized CSI: verb=%c <%s>\n", + verb, rt->pd->esbuf); +#endif + break; + } }