X-Git-Url: http://git.madism.org/?a=blobdiff_plain;f=madtty%2Finject.c;h=29755fb91610292894d42653aa7703780729f49b;hb=f825f4ca53c05c78e52b7e007e07ad76587fb46c;hp=7334921b0995bbc9efc92d29d7af010959697205;hpb=8ead06c97e16165208fd9d02ada73d63aeca11f2;p=apps%2Fmadtty.git diff --git a/madtty/inject.c b/madtty/inject.c index 7334921..29755fb 100644 --- a/madtty/inject.c +++ b/madtty/inject.c @@ -21,19 +21,44 @@ #include #include +#include #include "madtty.h" -#include "roteprivate.h" -#include "inject_csi.h" +#include "madtty_priv.h" + +#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; + if (rt->crow <= rt->pd->scrollbottom) + return; - /* must scroll the scrolling region up by 1 line, and put cursor on + /* must scroll the scrolling region up by 1 line, and put cursor on * last line of it */ rt->crow = rt->pd->scrollbottom; @@ -46,20 +71,21 @@ static void cursor_line_down(RoteTerm *rt) /* 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].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; + if (rt->crow >= rt->pd->scrolltop) + return; - /* must scroll the scrolling region up by 1 line, and put cursor on + /* must scroll the scrolling region up by 1 line, and put cursor on * first line of it */ rt->crow = rt->pd->scrolltop; @@ -72,13 +98,12 @@ static void cursor_line_up(RoteTerm *rt) /* 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].ch = 0x20; rt->cells[rt->pd->scrolltop][i].attr = 0x70; } - } -static inline void put_normal_char(RoteTerm *rt, char c) +static inline void put_normal_char(RoteTerm *rt, int c) { if (rt->ccol >= rt->cols) { rt->ccol = 0; @@ -88,11 +113,12 @@ static inline void put_normal_char(RoteTerm *rt, char c) if (rt->insert) { int i; - for(i = rt->cols - 1; i >= rt->ccol+1; i--) + 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].ch = c; rt->cells[rt->crow][rt->ccol].attr = rt->curattr; rt->ccol++; @@ -100,12 +126,12 @@ static inline void put_normal_char(RoteTerm *rt, char c) rt->curpos_dirty = true; } -static inline void put_graphmode_char(RoteTerm *rt, char c) +static inline void put_graphmode_char(RoteTerm *rt, int 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 'j': case 'k': case 'l': case 'm': case 'n': case 't': case 'u': case 'v': case 'w': nc = '+'; break; case 'x': @@ -131,54 +157,63 @@ static inline void cancel_escape_sequence(RoteTerm *rt) rt->pd->esbuf[0] = '\0'; } -static void handle_control_char(RoteTerm *rt, char c) +static void handle_control_char(RoteTerm *rt, int c) { switch (c) { - case '\r': rt->ccol = 0; break; /* carriage return */ + case '\r': /* carriage return */ + rt->ccol = 0; + break; + 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; - case '\x1B': /* begin escape sequence (aborting previous one if any) */ - new_escape_sequence(rt); - break; - case '\x0E': /* enter graphical character mode */ - rt->pd->graphmode = true; - break; - case '\x0F': /* exit graphical character mode */ - rt->pd->graphmode = false; - break; - case '\x9B': /* CSI character. Equivalent to ESC [ */ - 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; + rt->ccol = (rt->ccol + 8) & ~7; + clamp_cursor_to_bounds(rt); + break; + + case '\x1b': /* begin escape sequence (aborting previous one if any) */ + new_escape_sequence(rt); + break; + + case '\x0e': /* enter graphical character mode */ + rt->pd->graphmode = true; + break; + + case '\x0f': /* exit graphical character mode */ + rt->pd->graphmode = false; + break; + + case '\x9b': /* CSI character. Equivalent to ESC [ */ + 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; + case '\a': /* bell */ - /* 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 + /* do nothing for now... maybe a visual bell would be nice? */ + break; } } -static inline bool is_valid_csi_ender(char c) +static inline bool is_valid_csi_ender(int c) { - return (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - c == '@' || c == '`'; + return (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c == '@' || c == '`'); } static void try_interpret_escape_seq(RoteTerm *rt) @@ -186,42 +221,8 @@ 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 - } + if (!firstchar) + return; /* too early to do anything */ /* interpret ESC-M as reverse line-feed */ if (firstchar == 'M') { @@ -232,10 +233,6 @@ static void try_interpret_escape_seq(RoteTerm *rt) 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; } @@ -244,28 +241,23 @@ static void try_interpret_escape_seq(RoteTerm *rt) /* we have a csi escape sequence: interpret it */ rote_es_interpret_csi(rt); cancel_escape_sequence(rt); - } - else if (firstchar == ']' && lastchar == '\a') { + } 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); + 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 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) { + for (; len-- > 0; data++) { + if ((unsigned char)*data <= 31) { handle_control_char(rt, *data); continue; } @@ -276,11 +268,390 @@ void rote_vt_inject(RoteTerm *rt, const char *data, int len) rt->pd->esbuf[++rt->pd->esbuf_len] = 0; try_interpret_escape_seq(rt); - } - else if (rt->pd->graphmode) + } else + if (rt->pd->graphmode) { put_graphmode_char(rt, *data); - else + } else { put_normal_char(rt, *data); + } + } + + return 0; +} + +/****************************************************************************/ +/* 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 */ + } + + 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[] __attribute__((unused)), + int pcount __attribute__((unused))) +{ + rt->pd->saved_x = rt->ccol; + rt->pd->saved_y = rt->crow; +} + +static void +interpret_csi_RESTORECUR(RoteTerm *rt, + int param[] __attribute__((unused)), + int pcount __attribute__((unused))) +{ + 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 */ + return; + } + + /* parse numeric parameters */ + while (isdigit((unsigned char)*p) || *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: + break; } }