From 59f26adf68f69976f2f408d45ce536bb85c1f3cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 20 Aug 2017 14:42:38 +0200 Subject: [PATCH] Stubs for scrolling region and other functions, implemented inverse video, improved parse (hardeing against xterm travesty), optimize tabs, DECREPTPARM --- html_orig/js/app.js | 20 +- html_orig/jssrc/term.js | 20 +- user/ansi_parser.c | 4 +- user/ansi_parser.rl | 4 +- user/ansi_parser_callbacks.c | 350 ++++++++++++++++++++++++++++------- user/ascii.h | 16 +- user/screen.c | 140 +++++++++----- user/screen.h | 163 ++++++++++------ user/user_main.h | 7 +- 9 files changed, 520 insertions(+), 204 deletions(-) diff --git a/html_orig/js/app.js b/html_orig/js/app.js index ab6411b..6e819a2 100644 --- a/html_orig/js/app.js +++ b/html_orig/js/app.js @@ -1693,8 +1693,8 @@ var Screen = (function () { cell = { t: ' ', - fg: cursor.fg, - bg: cursor.bg, + fg: 7, + bg: 0, // the colors will be replaced immediately as we receive data (user won't see this) attrs: 0, e: e, x: i % W, @@ -1768,20 +1768,18 @@ var Screen = (function () { // Attributes num = parse2B(str, i); i += 2; // fg bg bold hidden - cursor.fg = num & 0x0F; - cursor.bg = (num & 0xF0) >> 4; - cursor.hidden = !(num & 0x100); - cursor.hanging = !!(num & 0x200); + cursor.hidden = !(num & 0x0001); + cursor.hanging = !!(num & 0x0002); // console.log("Attributes word ",num.toString(16)+'h'); Input.setAlts( - !!(num & 0x400), // cu - !!(num & 0x800), // np - !!(num & 0x1000) // fn + !!(num & 0x0004), // cu + !!(num & 0x0008), // np + !!(num & 0x0010) // fn ); - fg = cursor.fg; - bg = cursor.bg; + fg = 7; + bg = 0; attrs = 0; // Here come the content diff --git a/html_orig/jssrc/term.js b/html_orig/jssrc/term.js index 214a5c2..28627d6 100644 --- a/html_orig/jssrc/term.js +++ b/html_orig/jssrc/term.js @@ -136,8 +136,8 @@ var Screen = (function () { cell = { t: ' ', - fg: cursor.fg, - bg: cursor.bg, + fg: 7, + bg: 0, // the colors will be replaced immediately as we receive data (user won't see this) attrs: 0, e: e, x: i % W, @@ -211,20 +211,18 @@ var Screen = (function () { // Attributes num = parse2B(str, i); i += 2; // fg bg bold hidden - cursor.fg = num & 0x0F; - cursor.bg = (num & 0xF0) >> 4; - cursor.hidden = !(num & 0x100); - cursor.hanging = !!(num & 0x200); + cursor.hidden = !(num & 0x0001); + cursor.hanging = !!(num & 0x0002); // console.log("Attributes word ",num.toString(16)+'h'); Input.setAlts( - !!(num & 0x400), // cu - !!(num & 0x800), // np - !!(num & 0x1000) // fn + !!(num & 0x0004), // cu + !!(num & 0x0008), // np + !!(num & 0x0010) // fn ); - fg = cursor.fg; - bg = cursor.bg; + fg = 7; + bg = 0; attrs = 0; // Here come the content diff --git a/user/ansi_parser.c b/user/ansi_parser.c index b677b50..9786271 100644 --- a/user/ansi_parser.c +++ b/user/ansi_parser.c @@ -155,7 +155,7 @@ ansi_parser(char newchar) return; case TAB: - screen_tab_forward(); + screen_tab_forward(1); return; // Select G0 or G1 @@ -405,7 +405,7 @@ execFuncs: /* #line 202 "user/ansi_parser.rl" */ { // Reset the CSI builder - leadchar = 0; + leadchar = NUL; arg_ni = 0; arg_cnt = 0; diff --git a/user/ansi_parser.rl b/user/ansi_parser.rl index 6d1654b..77b34a4 100644 --- a/user/ansi_parser.rl +++ b/user/ansi_parser.rl @@ -124,7 +124,7 @@ ansi_parser(char newchar) return; case TAB: - screen_tab_forward(); + screen_tab_forward(1); return; // Select G0 or G1 @@ -201,7 +201,7 @@ ansi_parser(char newchar) action CSI_start { // Reset the CSI builder - leadchar = 0; + leadchar = NUL; arg_ni = 0; arg_cnt = 0; diff --git a/user/ansi_parser_callbacks.c b/user/ansi_parser_callbacks.c index ddb3f92..cbfc3ad 100644 --- a/user/ansi_parser_callbacks.c +++ b/user/ansi_parser_callbacks.c @@ -5,12 +5,16 @@ */ #include +#include #include "ansi_parser_callbacks.h" #include "screen.h" #include "ansi_parser.h" #include "uart_driver.h" #include "sgr.h" #include "cgi_sockets.h" +#include "ascii.h" +#include "user_main.h" +#include "syscfg.h" static char utf_collect[4]; static int utf_i = 0; @@ -131,6 +135,37 @@ apars_handle_bel(void) send_beep(); } +// data tables for the DECREPTPARM command response + +struct DECREPTPARM_parity {int parity; const char * msg;}; +static const struct DECREPTPARM_parity DECREPTPARM_parity_arr[] = { + {PARITY_NONE, "1"}, + {PARITY_ODD, "4"}, + {PARITY_EVEN, "5"}, + {-1, 0} +}; + +struct DECREPTPARM_baud {int baud; const char * msg;}; +static const struct DECREPTPARM_baud DECREPTPARM_baud_arr[] = { + {BIT_RATE_300, "48"}, + {BIT_RATE_600, "56"}, + {BIT_RATE_1200, "64"}, + {BIT_RATE_2400, "88"}, + {BIT_RATE_4800, "96"}, + {BIT_RATE_9600 , "104"}, + {BIT_RATE_19200 , "112"}, + {BIT_RATE_38400 , "120"}, + {BIT_RATE_57600 , "128"}, // this is the last in the spec, follow +8 + {BIT_RATE_74880 , "136"}, + {BIT_RATE_115200, "144"}, + {BIT_RATE_230400, "152"}, + {BIT_RATE_460800, "160"}, + {BIT_RATE_921600, "168"}, + {BIT_RATE_1843200, "176"}, + {BIT_RATE_3686400, "184"}, + {-1, 0} +}; + /** * Handle fully received CSI ANSI sequence * @param leadchar - private range leading character, 0 if none @@ -143,10 +178,10 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar) int n1 = params[0]; int n2 = params[1]; int n3 = params[2]; - static char buf[20]; - bool yn = 0; // for ? l h + char buf[32]; + bool yn = false; // for ? l h - // defaults + // defaults - FIXME this may inadvertently affect some variants that should be left unchanged switch (keychar) { case 'A': // move case 'B': @@ -166,6 +201,7 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar) case 'P': case 'I': case 'Z': + case 'b': if (n1 == 0) n1 = 1; break; @@ -215,6 +251,11 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar) screen_cursor_set_x(0); break; + case 'b': + // TODO repeat preceding graphic character n1 times + ansi_warn("NOIMPL: Repeat char"); + return; + // Set X case 'G': case '`': // alternate code @@ -232,8 +273,66 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar) break; // 1-based // SU, SD - scroll up/down - case 'S': screen_scroll_up(n1); break; - case 'T': screen_scroll_down(n1); break; + case 'S': + if (leadchar == NUL && count <= 1) { + screen_scroll_up(n1); + } + else { + // other: + // CSI ? Pi; Pa; Pv S (sixel) + ansi_warn("NOIMPL: CSI"); + apars_handle_badseq(); + } + break; + case 'T': + if (leadchar == NUL && count <= 1) { + // CSI Ps T + screen_scroll_down(n1); + } + else { + // other: + // CSI Ps ; Ps ; Ps ; Ps ; Ps T + // CSI > Ps; Ps T + ansi_warn("NOIMPL: CSI"); + apars_handle_badseq(); + } + break; + + case 't': // xterm window commands + if (leadchar == NUL && count <= 2) { + // CSI Ps ; Ps ; Ps t + switch (n1) { + case 8: // set size + screen_resize(n2, n3); + break; + case 18: // report size + printf(buf, "\033[8;%d;%dt", termconf_scratch.height, termconf_scratch.width); + respond(buf); + break; + case 11: // Report iconified -> is not iconified + respond("\033[1t"); + break; + case 21: // Report title + respond("\033]L"); + respond(termconf_scratch.title); + respond("\033\\"); + break; + case 24: // Set Height only + screen_resize(n2, termconf_scratch.width); + break; + default: + ansi_warn("NOIMPL CSI %d t", n1); + break; + } + } + else { + // other: + // CSI > Ps; Ps t + // CSI Ps SP t, + ansi_warn("NOIMPL: CSI"); + apars_handle_badseq(); + } + break; // CUP,HVP - set position case 'H': @@ -242,6 +341,11 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar) break; // 1-based case 'J': // Erase screen + if (leadchar == '?') { + // TODO selective erase + ansi_warn("NOIMPL: Selective erase"); + } + if (n1 == 0) { screen_clear(CLEAR_FROM_CURSOR); } else if (n1 == 1) { @@ -253,6 +357,11 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar) break; case 'K': // Erase lines + if (leadchar == '?') { + // TODO selective erase + ansi_warn("NOIMPL: Selective erase"); + } + if (n1 == 0) { screen_clear_line(CLEAR_FROM_CURSOR); } else if (n1 == 1) { @@ -263,10 +372,37 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar) break; // SCP, RCP - save/restore position - case 's': screen_cursor_save(0); break; - case 'u': screen_cursor_restore(0); break; + case 's': + if (leadchar == NUL && count == 0) { + screen_cursor_save(0); + } + else { + // other: + // CSI ? Pm s + // CSI Pl; Pr s + ansi_warn("NOIMPL: CSI"); + apars_handle_badseq(); + } + break; + + case 'u': + if (leadchar == NUL && count == 0) { + screen_cursor_restore(0); + } + else { + ansi_warn("NOIMPL: CSI"); + apars_handle_badseq(); + } + break; case 'n': // Queries + if (leadchar == '>') { + // some xterm garbage - discard + // CSI > Ps n + ansi_warn("NOIMPL: CSI > %d n", n1); + break; + } + if (n1 == 6) { // Query cursor position int x, y; @@ -279,29 +415,47 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar) respond("\033[0n"); } else { - ansi_warn("NOIMPL: CSI %d n", n1); + ansi_warn("NOIMPL: CSI"); + apars_handle_badseq(); } break; case 'h': // DEC feature enable yn = 1; case 'l': // DEC feature disable + // yn is 0 by default for (int i = 0; i < count; i++) { int n = params[i]; if (leadchar == '?') { if (n == 1) { screen_set_cursors_alt_mode(yn); } + else if (n == 2) { + // should reset all Gx to USASCII and reset to VT100 (which we use always) + screen_set_charset(0, 'B'); + screen_set_charset(1, 'B'); + } + else if (n == 3) { + // TODO 132 column mode - not implemented due to RAM demands + ansi_warn("NOIMPL: 80->132"); + } + else if (n == 4) { + // Smooth scroll - not implemented + } + else if (n == 5) { + screen_set_reverse_video(yn); + } else if (n == 6) { - // TODO origin mode (scrolling region) + screen_set_origin_mode(yn); } else if (n == 7) { screen_wrap_enable(yn); } else if (n == 8) { - // Key auto-repeat + // TODO Key auto-repeat // We don't implement this currently, but it could be added // - discard repeated keypress events between keydown and keyup. + ansi_warn("NOIMPL: Auto-repeat toggle"); } else if (n == 9 || (n >= 1000 && n <= 1006)) { // TODO mouse @@ -312,20 +466,60 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar) // 1004 - Send FocusIn/FocusOut events // 1005 - Enable UTF-8 Mouse Mode // 1006 - SGR mouse mode + ansi_warn("NOIMPL: Mouse tracking"); } else if (n == 12) { // TODO Cursor blink on/off + ansi_warn("NOIMPL: Cursor blink toggle"); + } + else if (n == 40) { + // TODO allow/disallow 80->132 mode + // not implemented because of RAM demands + ansi_warn("NOIMPL: 80->132 enable"); + } + else if (n == 47 || n == 1047) { + // Switch to/from alternate screen + // - not implemented fully due to RAM demands + screen_swap_state(yn); + } + else if (n == 1048) { + // same as DECSC - save/restore cursor with attributes + if (yn) { + screen_cursor_save(true); + } + else { + screen_cursor_restore(true); + } } else if (n == 1049) { - // XTERM: optionally switch to/from alternate screen - // We can't implement this because of limited RAM size + // save/restore cursor and screen and clear it + if (yn) { + screen_cursor_save(true); + screen_swap_state(true); // this should save the screen - can't because of RAM size + screen_clear(CLEAR_ALL); + } + else { + screen_clear(CLEAR_ALL); + screen_swap_state(false); // this should restore the screen - can't because of RAM size + screen_cursor_restore(true); + } + } + else if (n >= 1050 && n <= 1053) { + // TODO Different kinds of function key emulation ? + // (In practice this seems hardly ever used) + + // Ps = 1 0 5 0 -> Set terminfo/termcap function-key mode. + // Ps = 1 0 5 1 -> Set Sun function-key mode. + // Ps = 1 0 5 2 -> Set HP function-key mode. + // Ps = 1 0 5 3 -> Set SCO function-key mode. + ansi_warn("NOIMPL: FN key emul type"); } else if (n == 2004) { // Bracketed paste mode // Discard, we don't implement this } else if (n == 25) { - screen_cursor_visible(yn); + screen_set_cursor_visible(yn); } else { ansi_warn("NOIMPL: CSI ? %d %c", n, keychar); @@ -350,6 +544,12 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar) count = 1; // this makes it work as 0 (reset) } + if (leadchar == '>') { + // some xterm garbage - discard + // CSI > Ps; Ps m + break; + } + // iterate arguments for (int i = 0; i < count; i++) { int n = params[i]; @@ -361,22 +561,22 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar) else if (n == SGR_FG_DEFAULT) screen_set_fg(termconf_scratch.default_fg); // default fg else if (n == SGR_BG_DEFAULT) screen_set_bg(termconf_scratch.default_bg); // default bg // -- set attr -- - else if (n == SGR_BOLD) screen_attr_enable(ATTR_BOLD); - else if (n == SGR_FAINT) screen_attr_enable(ATTR_FAINT); - else if (n == SGR_ITALIC) screen_attr_enable(ATTR_ITALIC); - else if (n == SGR_UNDERLINE) screen_attr_enable(ATTR_UNDERLINE); - else if (n == SGR_BLINK || n == SGR_BLINK_FAST) screen_attr_enable(ATTR_BLINK); // 6 - rapid blink, not supported - else if (n == SGR_INVERSE) screen_inverse_enable(true); - else if (n == SGR_STRIKE) screen_attr_enable(ATTR_STRIKE); - else if (n == SGR_FRAKTUR) screen_attr_enable(ATTR_FRAKTUR); + else if (n == SGR_BOLD) screen_set_sgr(ATTR_BOLD, 1); + else if (n == SGR_FAINT) screen_set_sgr(ATTR_FAINT, 1); + else if (n == SGR_ITALIC) screen_set_sgr(ATTR_ITALIC, 1); + else if (n == SGR_UNDERLINE) screen_set_sgr(ATTR_UNDERLINE, 1); + else if (n == SGR_BLINK || n == SGR_BLINK_FAST) screen_set_sgr(ATTR_BLINK, 1); // 6 - rapid blink, not supported + else if (n == SGR_STRIKE) screen_set_sgr(ATTR_STRIKE, 1); + else if (n == SGR_FRAKTUR) screen_set_sgr(ATTR_FRAKTUR, 1); + else if (n == SGR_INVERSE) screen_set_sgr_inverse(1); // -- clear attr -- - else if (n == SGR_OFF(SGR_BOLD)) screen_attr_disable(ATTR_BOLD); - else if (n == SGR_OFF(SGR_FAINT)) screen_attr_disable(ATTR_FAINT); - else if (n == SGR_OFF(SGR_ITALIC)) screen_attr_disable(ATTR_ITALIC|ATTR_FRAKTUR); - else if (n == SGR_OFF(SGR_UNDERLINE)) screen_attr_disable(ATTR_UNDERLINE); - else if (n == SGR_OFF(SGR_BLINK)) screen_attr_disable(ATTR_BLINK); - else if (n == SGR_OFF(SGR_INVERSE)) screen_inverse_enable(false); - else if (n == SGR_OFF(SGR_STRIKE)) screen_attr_disable(ATTR_STRIKE); + else if (n == SGR_OFF(SGR_BOLD)) screen_set_sgr(ATTR_BOLD, 0); + else if (n == SGR_OFF(SGR_FAINT)) screen_set_sgr(ATTR_FAINT, 0); + else if (n == SGR_OFF(SGR_ITALIC)) screen_set_sgr(ATTR_ITALIC | ATTR_FRAKTUR, 0); // there is no dedicated OFF code for Fraktur + else if (n == SGR_OFF(SGR_UNDERLINE)) screen_set_sgr(ATTR_UNDERLINE, 0); + else if (n == SGR_OFF(SGR_BLINK)) screen_set_sgr(ATTR_BLINK, 0); + else if (n == SGR_OFF(SGR_STRIKE)) screen_set_sgr(ATTR_STRIKE, 0); + else if (n == SGR_OFF(SGR_INVERSE)) screen_set_sgr_inverse(0); // -- AIX bright colors -- else if (n >= SGR_FG_BRT_START && n <= SGR_FG_BRT_END) screen_set_fg((Color) ((n - SGR_FG_BRT_START) + 8)); // AIX bright fg else if (n >= SGR_BG_BRT_START && n <= SGR_BG_BRT_END) screen_set_bg((Color) ((n - SGR_BG_BRT_START) + 8)); // AIX bright bg @@ -386,32 +586,6 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar) } break; - case 't': // xterm window hacks - switch(n1) { - case 8: // set size - screen_resize(n2, n3); - break; - case 18: // report size - printf(buf, "\033[8;%d;%dt", termconf_scratch.height, termconf_scratch.width); - respond(buf); - break; - case 11: // Report iconified -> is not iconified - respond("\033[1t"); - break; - case 21: // Report title - respond("\033]L"); - respond(termconf_scratch.title); - respond("\033\\"); - break; - case 24: // Set Height only - screen_resize(n2, termconf_scratch.width); - break; - default: - ansi_warn("NOIMPL CSI %d t", n1); - break; - } - break; - case 'L': // Insert lines (shove down) screen_insert_lines(n1); break; @@ -429,7 +603,16 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar) break; case 'r': - // TODO scrolling region + if (leadchar == NUL && count == 2) { + screen_set_scrolling_region(n1, n2); + } + else { + // other: + // CSI ? Pm r + // CSI Pt; Pl; Pb; Pr; Ps$ r + ansi_warn("NOIMPL: CSI"); + apars_handle_badseq(); + } break; case 'g': // Clear tabs @@ -441,19 +624,55 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar) break; case 'Z': // Tab backward - for(; n1 > 0; n1--) { - screen_tab_reverse(); - } + screen_tab_reverse(n1); break; case 'I': // Tab forward - for(; n1 > 0; n1--) { - screen_tab_forward(); - } + screen_tab_forward(n1); break; case 'c': // CSI-c - report capabilities - respond("\033[?64;22;c"); // pretend we're vt400 + if (leadchar == NUL) { + respond("\033[?64;9c"); // pretend we're vt400 with national character sets + } + else if (leadchar == '>') { + // 41 - we're "VT400", 0 - ROM cartridge number + sprintf(buf, "\033[>41;%d;0c", FIRMWARE_VERSION_NUM); + respond(buf); + } else { + ansi_warn("NOIMPL: CSI"); + apars_handle_badseq(); + } + break; + + case 'x': // DECREPTPARM + if (n1 <= 1) { + respond("\033[2;"); // this is a response + + // Parity + for(const struct DECREPTPARM_parity *p = DECREPTPARM_parity_arr; p->parity != -1; p++) { + if (p->parity == sysconf->uart_parity) { + respond(p->msg); + break; + } + } + + // bits per character (uart byte) + respond(";8;"); + + // Baud rate + for(const struct DECREPTPARM_baud *p = DECREPTPARM_baud_arr; p->baud != -1; p++) { + if (p->baud == sysconf->uart_baudrate) { + respond(p->msg); + respond(";"); + respond(p->msg); + break; + } + } + + // multiplier 1, flags 0 + respond(";1;0x"); // ROM cartridge number ?? + } break; default: @@ -469,6 +688,13 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar) void ICACHE_FLASH_ATTR apars_handle_hashCode(char c) { switch(c) { + case '3': // Double size, top half + case '4': // Single size, bottom half + case '5': // Single width, single height + case '6': // Double width + ansi_warn("NOIMPL: Double Size Line"); + break; + case '8': screen_fill_with_E(); break; diff --git a/user/ascii.h b/user/ascii.h index a0deec3..55f5cdd 100644 --- a/user/ascii.h +++ b/user/ascii.h @@ -13,13 +13,13 @@ enum ASCII_CODES { EOT = 4, ENQ = 5, ACK = 6, - BEL = 7, - BS = 8, - TAB = 9, - LF = 10, - VT = 11, - FF = 12, - CR = 13, + BEL = 7, /* \a */ + BS = 8, /* \b */ + TAB = 9, /* \t */ + LF = 10, /* \n */ + VT = 11, /* \v */ + FF = 12, /* \f */ + CR = 13, /* \r */ SO = 14, SI = 15, DLE = 16, @@ -33,7 +33,7 @@ enum ASCII_CODES { CAN = 24, EM = 25, SUB = 26, - ESC = 27, + ESC = 27, /* \033, \x1b, \e */ FS = 28, GS = 29, RS = 30, diff --git a/user/screen.c b/user/screen.c index 8cc75f6..83e6bc3 100644 --- a/user/screen.c +++ b/user/screen.c @@ -3,6 +3,7 @@ #include "screen.h" #include "persist.h" #include "sgr.h" +#include "ascii.h" TerminalConfigBundle * const termconf = &persist.current.termconf; TerminalConfigBundle termconf_scratch; @@ -48,6 +49,7 @@ static struct { bool cursors_alt_mode; //!< Application mode for cursor keys bool newline_mode; + bool reverse; char charset0; char charset1; @@ -205,6 +207,7 @@ screen_reset(void) scr.numpad_alt_mode = false; scr.cursors_alt_mode = false; scr.newline_mode = false; + scr.reverse = false; scr.charset0 = 'B'; scr.charset1 = '0'; @@ -216,6 +219,18 @@ screen_reset(void) NOTIFY_DONE(); } + +/** + * Swap screen buffer / state + * this is CSI ? 47/1047/1049 h/l + * @param alternate - true if we're swapping, false if unswapping + */ +void ICACHE_FLASH_ATTR +screen_swap_state(bool alternate) +{ + // TODO impl - backup/restore title, size, global attributes (not SGR) +} + //endregion //region --- Tab stops --- @@ -305,27 +320,32 @@ prev_tab_stop(void) } void ICACHE_FLASH_ATTR -screen_tab_forward(void) +screen_tab_forward(int count) { NOTIFY_LOCK(); - int tab = next_tab_stop(); - if (tab != -1) { - cursor.x = tab; - } else { - cursor.x = W-1; + for (; count > 0; count--) { + 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) +screen_tab_reverse(int count) { NOTIFY_LOCK(); - int tab = prev_tab_stop(); - if (tab != -1) { - cursor.x = tab; - } else { - cursor.x = 0; + for (; count > 0; count--) { + int tab = prev_tab_stop(); + if (tab != -1) { + cursor.x = tab; + } else { + cursor.x = 0; + } } NOTIFY_DONE(); } @@ -600,6 +620,14 @@ screen_scroll_down(unsigned int lines) done: NOTIFY_DONE(); } + +/** Set scrolling region */ +void ICACHE_FLASH_ATTR +screen_set_scrolling_region(int from, int to) +{ + // TODO implement (also add to scr) +} + //endregion //region --- Cursor manipulation --- @@ -726,7 +754,7 @@ screen_cursor_restore(bool withAttrs) * Enable cursor display */ void ICACHE_FLASH_ATTR -screen_cursor_visible(bool visible) +screen_set_cursor_visible(bool visible) { NOTIFY_LOCK(); cursor.visible = visible; @@ -763,19 +791,18 @@ screen_set_bg(Color color) } void ICACHE_FLASH_ATTR -screen_attr_enable(u8 attrs) +screen_set_sgr(u8 attrs, bool ena) { - cursor.attrs |= attrs; -} - -void ICACHE_FLASH_ATTR -screen_attr_disable(u8 attrs) -{ - cursor.attrs &= ~attrs; + if (ena) { + cursor.attrs |= attrs; + } + else { + cursor.attrs &= ~attrs; + } } void ICACHE_FLASH_ATTR -screen_inverse_enable(bool ena) +screen_set_sgr_inverse(bool ena) { cursor.inverse = ena; } @@ -829,12 +856,26 @@ screen_set_cursors_alt_mode(bool alt_mode) NOTIFY_DONE(); } +void ICACHE_FLASH_ATTR +screen_set_reverse_video(bool reverse) +{ + NOTIFY_LOCK(); + scr.reverse = reverse; + NOTIFY_DONE(); +} + void ICACHE_FLASH_ATTR screen_set_newline_mode(bool nlm) { scr.newline_mode = nlm; } +void ICACHE_FLASH_ATTR +screen_set_origin_mode(bool region_origin) +{ + // TODO implement (also add to scr) +} + void ICACHE_FLASH_ATTR screen_report_sgr(char *buffer) { @@ -868,32 +909,31 @@ screen_putchar(const char *ch) // Special treatment for CRLF switch (ch[0]) { - case '\r': + case CR: screen_cursor_set_x(0); - if (scr.newline_mode) { - // like LF - screen_cursor_move(1, 0, true); - } goto done; - case '\n': + case LF: screen_cursor_move(1, 0, true); // can scroll + if (scr.newline_mode) { + // like CR + screen_cursor_set_x(0); + } goto done; - case 8: // BS + case BS: if (cursor.x > 0) { - // according to vttest, backspace should go to col 79 if "hanging" after 80 + // backspace should go to col 79 if "hanging" after 80 (as if it never actually left the 80th col) if (cursor.hanging) { cursor.hanging = false; } cursor.x--; } - // we should not wrap around - // and apparently backspace should not even clear the cell + // we should not wrap around, and backspace should not even clear the cell (verified in xterm) goto done; default: - if (ch[0] < ' ') { + if (ch[0] < SP) { // Discard warn("Ignoring control char %d", (int)ch[0]); goto done; @@ -902,7 +942,7 @@ screen_putchar(const char *ch) if (cursor.hanging) { // perform the scheduled wrap if hanging - // if autowrap = off, it overwrites the last char + // if auto-wrap = off, it overwrites the last char if (cursor.wraparound) { cursor.x = 0; cursor.y++; @@ -1137,13 +1177,11 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data) encode2B((u16) cursor.y, &w3); encode2B((u16) cursor.x, &w4); encode2B((u16) ( - cursor.fg | - (cursor.bg<<4) | - (cursor.visible ? 0x100 : 0) | - (cursor.hanging ? 0x200 : 0) | - (scr.cursors_alt_mode ? 0x400 : 0) | - (scr.numpad_alt_mode ? 0x800 : 0) | - (termconf->fn_alt_mode ? 0x1000 : 0) + (cursor.visible ? 0x01 : 0) | + (cursor.hanging ? 0x02 : 0) | + (scr.cursors_alt_mode ? 0x04 : 0) | + (scr.numpad_alt_mode ? 0x08 : 0) | + (termconf->fn_alt_mode ? 0x10 : 0) ) , &w5); @@ -1171,8 +1209,20 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data) // No repeat bool changeAttrs = cell0->attrs != ss->lastAttrs; bool changeColors = (cell0->fg != ss->lastFg || cell0->bg != ss->lastBg); + Color fg, bg; + + // Reverse fg and bg if we're in global reverse mode + if (! scr.reverse) { + fg = cell0->fg; + bg = cell0->bg; + } + else { + fg = cell0->bg; + bg = cell0->fg; + } + if (!changeAttrs && changeColors) { - encode2B(cell0->fg | (cell0->bg<<4), &w1); + encode2B(fg | (bg<<4), &w1); bufprint("\x03%c%c", w1.lsb, w1.msb); } else if (changeAttrs && !changeColors) { @@ -1182,11 +1232,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data) } else if (changeAttrs && changeColors) { // colors and attrs - encode3B((u32) ( - cell0->fg | - (cell0->bg<<4) | - (cell0->attrs<<8)) - , &lw1); + encode3B((u32) (fg | (bg<<4) | (cell0->attrs<<8)), &lw1); bufprint("\x01%c%c%c", lw1.lsb, lw1.msb, lw1.xsb); } diff --git a/user/screen.h b/user/screen.h index d745c62..4f4d75e 100644 --- a/user/screen.h +++ b/user/screen.h @@ -41,19 +41,6 @@ #define TERM_BTN_LEN 10 #define TERM_TITLE_LEN 64 -typedef enum { - CHANGE_CONTENT, - CHANGE_LABELS, -} ScreenNotifyChangeTopic; - -#define ATTR_BOLD (1<<0) -#define ATTR_FAINT (1<<1) -#define ATTR_ITALIC (1<<2) -#define ATTR_UNDERLINE (1<<3) -#define ATTR_BLINK (1<<4) -#define ATTR_FRAKTUR (1<<5) -#define ATTR_STRIKE (1<<6) - #define SCR_DEF_DISPLAY_TOUT_MS 20 #define SCR_DEF_PARSER_TOUT_MS 10 #define SCR_DEF_FN_ALT_MODE false @@ -64,10 +51,12 @@ typedef enum { /** Maximum screen size (determines size of the static data array) */ #define MAX_SCREEN_SIZE (80*25) +// --- Persistent Settings --- + typedef struct { u32 width; u32 height; - u8 default_bg; + u8 default_bg; // should be the Color typedef, but this way the size is more explicit u8 default_fg; char title[TERM_TITLE_LEN]; char btn[5][TERM_BTN_LEN]; @@ -86,21 +75,18 @@ extern TerminalConfigBundle * const termconf; */ extern TerminalConfigBundle termconf_scratch; +/** Restore default settings to termconf. Does not apply or copy to scratch. */ void terminal_restore_defaults(void); +/** Apply settings, redraw (clears the screen) */ void terminal_apply_settings(void); -void terminal_apply_settings_noclear(void); // the same, but with no screen reset / init - -void screen_report_sgr(char *buffer); - -typedef enum { - CLEAR_TO_CURSOR=0, CLEAR_FROM_CURSOR=1, CLEAR_ALL=2 -} ClearMode; - -typedef uint8_t Color; - -httpd_cgi_state screenSerializeToBuffer(char *buffer, size_t buf_len, void **data); +/** Apply settings, redraw (no clear - not resized) */ +void terminal_apply_settings_noclear(void); +/** Init the screen */ +void screen_init(void); +/** Change the screen size */ +void screen_resize(int rows, int cols); -void screenSerializeLabelsToBuffer(char *buffer, size_t buf_len); +// --- Encoding --- typedef struct { u8 lsb; @@ -116,15 +102,18 @@ typedef struct { /** Encode number to two nice ASCII bytes */ void encode2B(u16 number, WordB2 *stru); -/** Init the screen */ -void screen_init(void); -/** Change the screen size */ -void screen_resize(int rows, int cols); -/** Check if coord is valid */ -bool screen_isCoordValid(int y, int x); +httpd_cgi_state screenSerializeToBuffer(char *buffer, size_t buf_len, void **data); + +void screenSerializeLabelsToBuffer(char *buffer, size_t buf_len); // --- Clearing --- +typedef enum { + CLEAR_TO_CURSOR = 0, + CLEAR_FROM_CURSOR = 1, + CLEAR_ALL = 2 +} ClearMode; + /** Screen reset to default state */ void screen_reset(void); /** Clear entire screen */ @@ -133,18 +122,23 @@ void screen_clear(ClearMode mode); void screen_clear_line(ClearMode mode); /** Clear part of line */ void screen_clear_in_line(unsigned int count); -/** Shift screen upwards */ -void screen_scroll_up(unsigned int lines); -/** Shift screen downwards */ -void screen_scroll_down(unsigned int lines); -/** esc # 8 - fill entire screen with E of default colors (DEC alignment display) */ -void screen_fill_with_E(void); +/** Swap to alternate buffer (really what this does is backup terminal title, size and other global attributes) */ +void screen_swap_state(bool alternate); // --- insert / delete --- + +/** Insert lines at cursor, shove down */ void screen_insert_lines(unsigned int lines); +/** Delete lines at cursor, pull up */ void screen_delete_lines(unsigned int lines); +/** Insert characters at cursor, shove right */ void screen_insert_characters(unsigned int count); +/** Delete characters at cursor, pull left */ void screen_delete_characters(unsigned int count); +/** Shift screen upwards */ +void screen_scroll_up(unsigned int lines); +/** Shift screen downwards */ +void screen_scroll_down(unsigned int lines); // --- Cursor control --- @@ -156,51 +150,81 @@ void screen_cursor_get(int *y, int *x); void screen_cursor_set_x(int x); /** Set cursor Y position */ void screen_cursor_set_y(int y); -/** Reset cursor attribs */ -void screen_reset_sgr(void); /** Relative cursor move */ void screen_cursor_move(int dy, int dx, bool scroll); /** Save the cursor pos */ void screen_cursor_save(bool withAttrs); /** Restore the cursor pos */ void screen_cursor_restore(bool withAttrs); -/** Enable cursor display */ -void screen_cursor_visible(bool visible); -/** Enable auto wrap */ -void screen_wrap_enable(bool enable); + +// --- Cursor behavior setting --- + +/** Toggle INSERT / REPLACE */ +void screen_set_insert_mode(bool insert); /** Enable CR auto */ void screen_set_newline_mode(bool nlm); +/** Enable auto wrap */ +void screen_wrap_enable(bool enable); +/** Set scrolling region */ +void screen_set_scrolling_region(int from, int to); +/** Enable or disable origin remap to top left of scrolling region */ +void screen_set_origin_mode(bool region_origin); -// --- Colors --- +// --- Graphic rendition setting --- + +typedef uint8_t Color; // 0-16 + +#define ATTR_BOLD (1<<0) +#define ATTR_FAINT (1<<1) +#define ATTR_ITALIC (1<<2) +#define ATTR_UNDERLINE (1<<3) +#define ATTR_BLINK (1<<4) +#define ATTR_FRAKTUR (1<<5) +#define ATTR_STRIKE (1<<6) /** Set cursor foreground color */ void screen_set_fg(Color color); /** Set cursor background coloor */ void screen_set_bg(Color color); +/** Enable/disable attrs by bitmask */ +void screen_set_sgr(u8 attrs, bool ena); +/** Set the inverse attribute */ +void screen_set_sgr_inverse(bool ena); +/** Reset cursor attribs */ +void screen_reset_sgr(void); -/** enable attrs by bitmask */ -void screen_attr_enable(u8 attrs); -/** disable attrs by bitmask */ -void screen_attr_disable(u8 attrs); -/** Set the inverse cursor attribute */ -void screen_inverse_enable(bool ena); -/** Toggle INSERT / REPLACE */ -void screen_set_insert_mode(bool insert); +// --- Global modes and attributes --- + +/** Enable cursor display */ +void screen_set_cursor_visible(bool visible); /** Toggle application keypad mode */ void screen_set_numpad_alt_mode(bool app_mode); /** Toggle application cursor mode */ void screen_set_cursors_alt_mode(bool app_mode); +/** Set reverse video mode */ +void screen_set_reverse_video(bool reverse); + +// --- Charset --- +/** Switch G0 <-> G1 */ void screen_set_charset_n(int Gx); +/** Assign G0 or G1 */ void screen_set_charset(int Gx, char charset); -// tabs +// --- Tab stops --- +/** Remove all tabs on the screen */ void screen_clear_all_tabs(void); +/** Set tab at the current column */ void screen_set_tab(void); +/** Remove tab at the current column (if any) */ void screen_clear_tab(void); -void screen_tab_forward(void); -void screen_tab_reverse(void); +/** Move forward one tab */ +void screen_tab_forward(int count); +/** Move backward one tab */ +void screen_tab_reverse(int count); + +// --- Printing characters --- /** * Set a character in the cursor color, move to right with wrap. @@ -208,12 +232,31 @@ void screen_tab_reverse(void); * unicode (then it can be 4 chars, or terminated by a zero) */ void screen_putchar(const char *ch); +/** + * esc # 8 - fill entire screen with E of default colors + * (DEC alignment test mode) + */ +void screen_fill_with_E(void); + +// --- Queries --- + +/** Check if coord is valid */ +bool screen_isCoordValid(int y, int x); +/** Report current SGR as num;num;... for DAC query */ +void screen_report_sgr(char *buffer); -#if 0 -/** Debug dump */ -void screen_dd(void); -#endif +// --- Notify --- +typedef enum { + CHANGE_CONTENT = 0, + CHANGE_LABELS = 1, +} ScreenNotifyChangeTopic; + +/** + * Called when the screen content or settings change + * and the front-end should redraw / update. + * @param topic - what kind of change this is (chooses what message to send) + */ extern void screen_notifyChange(ScreenNotifyChangeTopic topic); #endif // SCREEN_H diff --git a/user/user_main.h b/user/user_main.h index 5f5ad7d..8067be5 100644 --- a/user/user_main.h +++ b/user/user_main.h @@ -1,7 +1,12 @@ #ifndef USER_MAIN_H_H #define USER_MAIN_H_H -#define FIRMWARE_VERSION "0.6.7+" GIT_HASH +#define FW_V_MAJOR 0 +#define FW_V_MINOR 6 +#define FW_V_PATCH 7 + +#define FIRMWARE_VERSION STR(FW_V_MAJOR) "." STR(FW_V_MINOR) "." STR(FW_V_PATCH) "+" GIT_HASH +#define FIRMWARE_VERSION_NUM (FW_V_MAJOR*10000 + FW_V_MINOR*100 + FW_V_PATCH) // this is used in ID queries #define TERMINAL_GITHUB_REPO "https://github.com/MightyPork/esp-vt100-firmware" #endif //USER_MAIN_H_H