From 68747d493c4d12bd821c3d1b435f71dee48062c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 19 Aug 2017 18:58:55 +0200 Subject: [PATCH] =?UTF-8?q?Tab=20Stops=E2=84=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- user/ansi_parser.c | 51 +++++++------ user/ansi_parser.rl | 5 +- user/ansi_parser_callbacks.c | 43 ++++++++--- user/screen.c | 140 +++++++++++++++++++++++++++++++---- user/screen.h | 8 ++ 5 files changed, 197 insertions(+), 50 deletions(-) diff --git a/user/ansi_parser.c b/user/ansi_parser.c index 22b8cb4..28f421b 100644 --- a/user/ansi_parser.c +++ b/user/ansi_parser.c @@ -150,11 +150,14 @@ ansi_parser(char newchar) newchar = LF; // translate to LF, like VT100 / xterm do case CR: case LF: - case TAB: case BS: apars_handle_plainchar(newchar); return; + case TAB: + screen_tab_forward(); + return; + // Select G0 or G1 case SI: screen_set_charset_n(1); @@ -202,7 +205,7 @@ ansi_parser(char newchar) // The parser -/* #line 206 "user/ansi_parser.c" */ +/* #line 209 "user/ansi_parser.c" */ { const char *_acts; unsigned int _nacts; @@ -488,7 +491,7 @@ execFuncs: while ( _nacts-- > 0 ) { switch ( *_acts++ ) { case 0: -/* #line 177 "user/ansi_parser.rl" */ +/* #line 180 "user/ansi_parser.rl" */ { if ((*p) != 0) { apars_handle_plainchar((*p)); @@ -496,7 +499,7 @@ execFuncs: } break; case 1: -/* #line 186 "user/ansi_parser.rl" */ +/* #line 189 "user/ansi_parser.rl" */ { // Reset the CSI builder csi_leading = csi_char = 0; @@ -512,13 +515,13 @@ execFuncs: } break; case 2: -/* #line 200 "user/ansi_parser.rl" */ +/* #line 203 "user/ansi_parser.rl" */ { csi_leading = (*p); } break; case 3: -/* #line 204 "user/ansi_parser.rl" */ +/* #line 207 "user/ansi_parser.rl" */ { if (csi_cnt == 0) csi_cnt = 1; // x10 + digit @@ -528,7 +531,7 @@ execFuncs: } break; case 4: -/* #line 212 "user/ansi_parser.rl" */ +/* #line 215 "user/ansi_parser.rl" */ { if (csi_cnt == 0) csi_cnt = 1; // handle case when first arg is empty csi_cnt++; @@ -536,7 +539,7 @@ execFuncs: } break; case 5: -/* #line 218 "user/ansi_parser.rl" */ +/* #line 221 "user/ansi_parser.rl" */ { csi_char = (*p); apars_handle_CSI(csi_leading, csi_n, csi_cnt, csi_char); @@ -544,7 +547,7 @@ execFuncs: } break; case 6: -/* #line 224 "user/ansi_parser.rl" */ +/* #line 227 "user/ansi_parser.rl" */ { ansi_warn("Parser error."); apars_handle_badseq(); @@ -552,7 +555,7 @@ execFuncs: } break; case 7: -/* #line 242 "user/ansi_parser.rl" */ +/* #line 245 "user/ansi_parser.rl" */ { csi_ni = 0; @@ -570,7 +573,7 @@ execFuncs: } break; case 8: -/* #line 259 "user/ansi_parser.rl" */ +/* #line 262 "user/ansi_parser.rl" */ { osc_bi = 0; osc_buffer[0] = '\0'; @@ -579,7 +582,7 @@ execFuncs: } break; case 9: -/* #line 266 "user/ansi_parser.rl" */ +/* #line 269 "user/ansi_parser.rl" */ { apars_handle_OSC_SetScreenSize(csi_n[0], csi_n[1]); inside_osc = false; @@ -587,13 +590,13 @@ execFuncs: } break; case 10: -/* #line 272 "user/ansi_parser.rl" */ +/* #line 275 "user/ansi_parser.rl" */ { osc_buffer[osc_bi++] = (*p); } break; case 11: -/* #line 276 "user/ansi_parser.rl" */ +/* #line 279 "user/ansi_parser.rl" */ { osc_buffer[osc_bi++] = '\0'; apars_handle_OSC_SetTitle(osc_buffer); @@ -602,7 +605,7 @@ execFuncs: } break; case 12: -/* #line 283 "user/ansi_parser.rl" */ +/* #line 286 "user/ansi_parser.rl" */ { osc_buffer[osc_bi++] = '\0'; apars_handle_OSC_SetButton(csi_n[0], osc_buffer); @@ -611,28 +614,28 @@ execFuncs: } break; case 13: -/* #line 316 "user/ansi_parser.rl" */ +/* #line 319 "user/ansi_parser.rl" */ { apars_handle_hashCode((*p)); {cs = 1;goto _again;} } break; case 14: -/* #line 321 "user/ansi_parser.rl" */ +/* #line 324 "user/ansi_parser.rl" */ { apars_handle_shortCode((*p)); {cs = 1;goto _again;} } break; case 15: -/* #line 326 "user/ansi_parser.rl" */ +/* #line 329 "user/ansi_parser.rl" */ { apars_handle_setXCtrls((*p)); // weird control settings like 7 bit / 8 bit mode {cs = 1;goto _again;} } break; case 16: -/* #line 331 "user/ansi_parser.rl" */ +/* #line 334 "user/ansi_parser.rl" */ { // abuse the buffer for storing the leading char osc_buffer[0] = (*p); @@ -640,13 +643,13 @@ execFuncs: } break; case 17: -/* #line 337 "user/ansi_parser.rl" */ +/* #line 340 "user/ansi_parser.rl" */ { apars_handle_characterSet(osc_buffer[0], (*p)); {cs = 1;goto _again;} } break; -/* #line 650 "user/ansi_parser.c" */ +/* #line 653 "user/ansi_parser.c" */ } } goto _again; @@ -664,7 +667,7 @@ _again: while ( __nacts-- > 0 ) { switch ( *__acts++ ) { case 6: -/* #line 224 "user/ansi_parser.rl" */ +/* #line 227 "user/ansi_parser.rl" */ { ansi_warn("Parser error."); apars_handle_badseq(); @@ -673,7 +676,7 @@ _again: goto _again;} } break; -/* #line 677 "user/ansi_parser.c" */ +/* #line 680 "user/ansi_parser.c" */ } } } @@ -681,7 +684,7 @@ goto _again;} _out: {} } -/* #line 361 "user/ansi_parser.rl" */ +/* #line 364 "user/ansi_parser.rl" */ } diff --git a/user/ansi_parser.rl b/user/ansi_parser.rl index 0840d5a..321deba 100644 --- a/user/ansi_parser.rl +++ b/user/ansi_parser.rl @@ -114,11 +114,14 @@ ansi_parser(char newchar) newchar = LF; // translate to LF, like VT100 / xterm do case CR: case LF: - case TAB: case BS: apars_handle_plainchar(newchar); return; + case TAB: + screen_tab_forward(); + return; + // Select G0 or G1 case SI: screen_set_charset_n(1); diff --git a/user/ansi_parser_callbacks.c b/user/ansi_parser_callbacks.c index b135d56..d12f308 100644 --- a/user/ansi_parser_callbacks.c +++ b/user/ansi_parser_callbacks.c @@ -136,6 +136,8 @@ apars_handle_CSI(char leadchar, int *params, int count, char keychar) case 'M': case '@': case 'P': + case 'I': + case 'Z': if (n1 == 0) n1 = 1; break; @@ -257,14 +259,26 @@ apars_handle_CSI(char leadchar, int *params, int count, char keychar) for (int i = 0; i < count; i++) { int n = params[i]; if (leadchar == '?') { - if (n == 25) { - screen_cursor_visible(yn); + if (n == 1) { + screen_set_cursors_alt_mode(yn); + } + else if (n == 6) { + // TODO origin mode (scrolling region) } else if (n == 7) { screen_wrap_enable(yn); } - else if (n == 1) { - screen_set_cursors_alt_mode(yn); + else if (n == 8) { + // TODO autorepeat mode + } + else if (n == 9) { + // TODO X10 mouse + } + else if (n == 1000) { + // TODo X11 mouse + } + else if (n == 25) { + screen_cursor_visible(yn); } else { // ansi_warn("NOIMPL DEC opt %d", n); @@ -371,13 +385,23 @@ apars_handle_CSI(char leadchar, int *params, int count, char keychar) break; case 'g': - // TODO clear tab -// ansi_warn("NOIMPL clear tab"); + if (n1 == 3) { + screen_clear_all_tabs(); + } else { + screen_clear_tab(); + } break; case 'Z': - // TODO back tab -// ansi_warn("NOIMPL cursor back tab"); + for(; n1 > 0; n1--) { + screen_tab_reverse(); + } + break; + + case 'I': + for(; n1 > 0; n1--) { + screen_tab_forward(); + } break; case 'c': // CSI-c @@ -438,8 +462,7 @@ void ICACHE_FLASH_ATTR apars_handle_shortCode(char c) break; case 'H': - // TODO set tab -// ansi_warn("NOIMPL set tab"); + screen_set_tab(); break; case '>': diff --git a/user/screen.c b/user/screen.c index e74aa64..75bbf01 100644 --- a/user/screen.c +++ b/user/screen.c @@ -32,6 +32,13 @@ typedef struct __attribute__((packed)){ */ static Cell screen[MAX_SCREEN_SIZE]; + +#define TABSTOP_WORDS 5 +/** + * Tab stops bitmap + */ +static u32 tab_stops[TABSTOP_WORDS]; + /** * Screen state structure */ @@ -204,10 +211,126 @@ screen_reset(void) // size is left unchanged screen_clear(CLEAR_ALL); + screen_clear_all_tabs(); + NOTIFY_DONE(); } //endregion +//region --- Tab stops --- + +void ICACHE_FLASH_ATTR +screen_clear_all_tabs(void) +{ + memset(tab_stops, 0, sizeof(tab_stops)); +} + +void ICACHE_FLASH_ATTR +screen_set_tab(void) +{ + tab_stops[cursor.x/32] |= (1<<(cursor.x%32)); +} + +void ICACHE_FLASH_ATTR +screen_clear_tab(void) +{ + tab_stops[cursor.x/32] &= ~(1<<(cursor.x%32)); +} + +/** + * Find tab stop closest to cursor to the right + * @return X pos or -1 + */ +static int ICACHE_FLASH_ATTR +next_tab_stop(void) +{ + // cursor must never go past EOL + if (cursor.x >= W-1) return -1; + + // find first word to inspect + int idx = (cursor.x+1)/32; + int offs = (cursor.x+1)%32; + int cp = cursor.x; + while (idx < TABSTOP_WORDS) { + u32 w = tab_stops[idx]; + w >>= offs; + for(;offs<32;offs++) { + cp++; + if (cp >= W) return -1; + if (w & 1) return cp; + w >>= 1; + } + offs = 0; + idx++; + } + + return -1; +} + +/** + * Find tab stop closest to cursor to the left + * @return X pos or -1 + */ +static int ICACHE_FLASH_ATTR +prev_tab_stop(void) +{ + // nowhere to go + if (cursor.x == 0) return -1; + + // find first word to inspect + int idx = (cursor.x-1)/32; + int offs = (cursor.x-1)%32; + int cp = cursor.x; + while (idx >= 0) { + u32 w = tab_stops[idx]; + w <<= 31-offs; + if (w == 0) { + cp -= cp%32; + if (cp < 0) return -1; + goto next; + } + for(;offs>=0;offs--) { + cp--; + if (cp < 0) return -1; + if (w & (1<<31)) return cp; + w <<= 1; + } + next: + offs = 31; + idx--; + } + + return -1; +} + +void ICACHE_FLASH_ATTR +screen_tab_forward(void) +{ + NOTIFY_LOCK(); + int tab = next_tab_stop(); + if (tab != -1) { + cursor.x = tab; + } else { + cursor.x = W-1; + } + NOTIFY_DONE(); +} + +void ICACHE_FLASH_ATTR +screen_tab_reverse(void) +{ + NOTIFY_LOCK(); + int tab = prev_tab_stop(); + if (tab != -1) { + cursor.x = tab; + } else { + cursor.x = 0; + } + NOTIFY_DONE(); +} + +//endregion + //region --- Clearing & inserting --- /** @@ -742,29 +865,16 @@ screen_putchar(const char *ch) case 8: // BS if (cursor.x > 0) { + // according to vttest, backspace should go to col 79 if "hanging" after 80 if (cursor.hanging) { cursor.hanging = false; - } else { - cursor.x--; } + cursor.x--; } // we should not wrap around // and apparently backspace should not even clear the cell goto done; - case 9: // TAB -// // TODO change to "go to next tab stop" -// if (cursor.x<((W-1)-(W-1)%4)) { -// c->c[0] = ' '; -// c->c[1] = 0; -// c->c[2] = 0; -// c->c[3] = 0; -// do { -// screen_putchar(" "); -// } while(cursor.x%4!=0); -// } - goto done; - default: if (ch[0] < ' ') { // Discard diff --git a/user/screen.h b/user/screen.h index 467cd0b..ac04573 100644 --- a/user/screen.h +++ b/user/screen.h @@ -193,6 +193,14 @@ void screen_set_cursors_alt_mode(bool app_mode); void screen_set_charset_n(int Gx); void screen_set_charset(int Gx, char charset); +// tabs + +void screen_clear_all_tabs(void); +void screen_set_tab(void); +void screen_clear_tab(void); +void screen_tab_forward(void); +void screen_tab_reverse(void); + /** * Set a character in the cursor color, move to right with wrap. * The character may be ASCII (then only one char is used), or