From 116201e2325fff7fc518d1ceaae0664553f63e65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Wed, 6 Sep 2017 20:54:20 +0200 Subject: [PATCH] Fixed a couple bugs and cleaned up the parser callbacks file for CSI --- user/ansi_parser.c | 210 ++++++----- user/ansi_parser.rl | 13 +- user/apars_csi.c | 845 +++++++++++++++++++++++++------------------- user/apars_csi.h | 2 +- user/screen.c | 61 ++-- user/screen.h | 2 - user/serial.c | 8 +- user/version.h | 2 +- 8 files changed, 649 insertions(+), 494 deletions(-) diff --git a/user/ansi_parser.c b/user/ansi_parser.c index 8345460..7cc8e9c 100644 --- a/user/ansi_parser.c +++ b/user/ansi_parser.c @@ -13,22 +13,22 @@ static const char _ansi_actions[] = { 0, 1, 0, 1, 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, 1, 8, 1, 9, 1, 10, 1, - 11, 1, 12, 1, 13, 1, 14, 2, - 3, 6 + 11, 1, 12, 1, 13, 1, 14, 1, + 15 }; static const char _ansi_eof_actions[] = { 0, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 0, 0, 0, 0, 0 + 1, 1, 1, 0, 0, 0, 0 }; static const int ansi_start = 1; -static const int ansi_first_final = 10; +static const int ansi_first_final = 11; static const int ansi_error = 0; static const int ansi_en_CSI_body = 5; -static const int ansi_en_STRCMD_body = 7; -static const int ansi_en_charsetcmd_body = 9; +static const int ansi_en_STRCMD_body = 8; +static const int ansi_en_charsetcmd_body = 10; static const int ansi_en_main = 1; @@ -101,6 +101,7 @@ ansi_parser(char newchar) { // The CSI code is built here static char leadchar; + static char interchar; // intermediate CSI char static int arg_ni; static int arg_cnt; static int arg[CSI_N_MAX]; @@ -113,12 +114,12 @@ ansi_parser(char newchar) // Init Ragel on the first run if (cs == -1) { -/* #line 117 "user/ansi_parser.c" */ +/* #line 118 "user/ansi_parser.c" */ { cs = ansi_start; } -/* #line 91 "user/ansi_parser.rl" */ +/* #line 92 "user/ansi_parser.rl" */ #if DEBUG_ANSI memset(history, 0, sizeof(history)); @@ -198,7 +199,7 @@ ansi_parser(char newchar) // The parser -/* #line 202 "user/ansi_parser.c" */ +/* #line 203 "user/ansi_parser.c" */ { const char *_acts; unsigned int _nacts; @@ -255,7 +256,7 @@ case 3: } else if ( (*p) >= 70 ) goto tr9; goto tr1; -case 10: +case 11: switch( (*p) ) { case 7: goto tr1; case 27: goto tr2; @@ -267,51 +268,63 @@ case 4: goto tr1; case 5: switch( (*p) ) { + case 36: goto tr11; case 59: goto tr13; - case 64: goto tr14; } - if ( (*p) < 60 ) { - if ( (*p) > 47 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto tr12; - } else if ( (*p) >= 32 ) + if ( (*p) < 48 ) { + if ( (*p) < 38 ) { + if ( 32 <= (*p) && (*p) <= 34 ) + goto tr11; + } else if ( (*p) > 39 ) { + if ( 41 <= (*p) && (*p) <= 45 ) + goto tr11; + } else goto tr11; - } else if ( (*p) > 63 ) { - if ( (*p) > 90 ) { - if ( 96 <= (*p) && (*p) <= 122 ) + } else if ( (*p) > 57 ) { + if ( (*p) < 64 ) { + if ( 61 <= (*p) && (*p) <= 63 ) + goto tr14; + } else if ( (*p) > 90 ) { + if ( 96 <= (*p) && (*p) <= 126 ) goto tr15; - } else if ( (*p) >= 65 ) + } else goto tr15; } else - goto tr11; + goto tr12; goto tr1; case 6: - if ( (*p) == 59 ) - goto tr13; - if ( (*p) < 64 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto tr12; - } else if ( (*p) > 90 ) { - if ( 96 <= (*p) && (*p) <= 122 ) + if ( (*p) > 90 ) { + if ( 96 <= (*p) && (*p) <= 126 ) goto tr15; - } else + } else if ( (*p) >= 64 ) goto tr15; goto tr1; -case 11: - goto tr1; case 12: - if ( (*p) == 59 ) - goto tr13; - if ( (*p) < 64 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto tr12; - } else if ( (*p) > 90 ) { - if ( 96 <= (*p) && (*p) <= 122 ) + goto tr1; +case 7: + switch( (*p) ) { + case 36: goto tr11; + case 59: goto tr13; + } + if ( (*p) < 41 ) { + if ( (*p) > 34 ) { + if ( 38 <= (*p) && (*p) <= 39 ) + goto tr11; + } else if ( (*p) >= 32 ) + goto tr11; + } else if ( (*p) > 45 ) { + if ( (*p) < 64 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr12; + } else if ( (*p) > 90 ) { + if ( 96 <= (*p) && (*p) <= 126 ) + goto tr15; + } else goto tr15; } else - goto tr15; + goto tr11; goto tr1; -case 7: +case 8: switch( (*p) ) { case 7: goto tr17; case 27: goto tr18; @@ -319,11 +332,11 @@ case 7: goto tr16; case 13: goto tr1; -case 8: +case 9: if ( (*p) == 92 ) goto tr17; goto tr1; -case 9: +case 10: switch( (*p) ) { case 7: goto tr1; case 27: goto tr1; @@ -339,44 +352,44 @@ case 14: tr3: cs = 3; goto _again; tr4: cs = 4; goto _again; tr11: cs = 6; goto f8; - tr12: cs = 6; goto f9; - tr13: cs = 6; goto f10; - tr16: cs = 7; goto f13; - tr18: cs = 8; goto _again; - tr5: cs = 10; goto f2; - tr6: cs = 10; goto f3; - tr7: cs = 10; goto f4; - tr8: cs = 10; goto f5; - tr9: cs = 10; goto f6; - tr10: cs = 10; goto f7; - tr15: cs = 11; goto f12; - tr14: cs = 12; goto f11; + tr12: cs = 7; goto f9; + tr13: cs = 7; goto f10; + tr14: cs = 7; goto f11; + tr16: cs = 8; goto f13; + tr18: cs = 9; goto _again; + tr5: cs = 11; goto f2; + tr6: cs = 11; goto f3; + tr7: cs = 11; goto f4; + tr8: cs = 11; goto f5; + tr9: cs = 11; goto f6; + tr10: cs = 11; goto f7; + tr15: cs = 12; goto f12; tr17: cs = 13; goto f14; tr19: cs = 14; goto f15; f0: _acts = _ansi_actions + 1; goto execFuncs; f1: _acts = _ansi_actions + 3; goto execFuncs; f5: _acts = _ansi_actions + 5; goto execFuncs; - f8: _acts = _ansi_actions + 7; goto execFuncs; + f11: _acts = _ansi_actions + 7; goto execFuncs; f9: _acts = _ansi_actions + 9; goto execFuncs; f10: _acts = _ansi_actions + 11; goto execFuncs; - f12: _acts = _ansi_actions + 13; goto execFuncs; - f4: _acts = _ansi_actions + 15; goto execFuncs; - f13: _acts = _ansi_actions + 17; goto execFuncs; - f14: _acts = _ansi_actions + 19; goto execFuncs; - f7: _acts = _ansi_actions + 21; goto execFuncs; - f3: _acts = _ansi_actions + 23; goto execFuncs; - f6: _acts = _ansi_actions + 25; goto execFuncs; - f2: _acts = _ansi_actions + 27; goto execFuncs; - f15: _acts = _ansi_actions + 29; goto execFuncs; - f11: _acts = _ansi_actions + 31; goto execFuncs; + f8: _acts = _ansi_actions + 13; goto execFuncs; + f12: _acts = _ansi_actions + 15; goto execFuncs; + f4: _acts = _ansi_actions + 17; goto execFuncs; + f13: _acts = _ansi_actions + 19; goto execFuncs; + f14: _acts = _ansi_actions + 21; goto execFuncs; + f7: _acts = _ansi_actions + 23; goto execFuncs; + f3: _acts = _ansi_actions + 25; goto execFuncs; + f6: _acts = _ansi_actions + 27; goto execFuncs; + f2: _acts = _ansi_actions + 29; goto execFuncs; + f15: _acts = _ansi_actions + 31; goto execFuncs; execFuncs: _nacts = *_acts++; while ( _nacts-- > 0 ) { switch ( *_acts++ ) { case 0: -/* #line 178 "user/ansi_parser.rl" */ +/* #line 179 "user/ansi_parser.rl" */ { ansi_warn("Parser error."); apars_show_context(); @@ -385,7 +398,7 @@ execFuncs: } break; case 1: -/* #line 187 "user/ansi_parser.rl" */ +/* #line 188 "user/ansi_parser.rl" */ { if ((*p) != 0) { apars_handle_plainchar((*p)); @@ -393,10 +406,11 @@ execFuncs: } break; case 2: -/* #line 195 "user/ansi_parser.rl" */ +/* #line 196 "user/ansi_parser.rl" */ { // Reset the CSI builder leadchar = NUL; + interchar = NUL; arg_ni = 0; arg_cnt = 0; @@ -409,13 +423,13 @@ execFuncs: } break; case 3: -/* #line 209 "user/ansi_parser.rl" */ +/* #line 211 "user/ansi_parser.rl" */ { leadchar = (*p); } break; case 4: -/* #line 213 "user/ansi_parser.rl" */ +/* #line 215 "user/ansi_parser.rl" */ { if (arg_cnt == 0) arg_cnt = 1; // x10 + digit @@ -425,7 +439,7 @@ execFuncs: } break; case 5: -/* #line 221 "user/ansi_parser.rl" */ +/* #line 223 "user/ansi_parser.rl" */ { if (arg_cnt == 0) arg_cnt = 1; // handle case when first arg is empty arg_cnt++; @@ -433,30 +447,36 @@ execFuncs: } break; case 6: -/* #line 227 "user/ansi_parser.rl" */ +/* #line 229 "user/ansi_parser.rl" */ { - apars_handle_csi(leadchar, arg, arg_cnt, (*p)); - {cs = 1;goto _again;} + interchar = (*p); } break; case 7: -/* #line 238 "user/ansi_parser.rl" */ +/* #line 233 "user/ansi_parser.rl" */ + { + apars_handle_csi(leadchar, arg, arg_cnt, interchar, (*p)); + {cs = 1;goto _again;} + } + break; + case 8: +/* #line 245 "user/ansi_parser.rl" */ { leadchar = (*p); str_ni = 0; string_buffer[0] = '\0'; inside_string = true; - {cs = 7;goto _again;} + {cs = 8;goto _again;} } break; - case 8: -/* #line 246 "user/ansi_parser.rl" */ + case 9: +/* #line 253 "user/ansi_parser.rl" */ { string_buffer[str_ni++] = (*p); } break; - case 9: -/* #line 250 "user/ansi_parser.rl" */ + case 10: +/* #line 257 "user/ansi_parser.rl" */ { inside_string = false; string_buffer[str_ni++] = '\0'; @@ -464,42 +484,42 @@ execFuncs: {cs = 1;goto _again;} } break; - case 10: -/* #line 263 "user/ansi_parser.rl" */ + case 11: +/* #line 270 "user/ansi_parser.rl" */ { apars_handle_hash_cmd((*p)); {cs = 1;goto _again;} } break; - case 11: -/* #line 268 "user/ansi_parser.rl" */ + case 12: +/* #line 275 "user/ansi_parser.rl" */ { apars_handle_short_cmd((*p)); {cs = 1;goto _again;} } break; - case 12: -/* #line 273 "user/ansi_parser.rl" */ + case 13: +/* #line 280 "user/ansi_parser.rl" */ { apars_handle_space_cmd((*p)); {cs = 1;goto _again;} } break; - case 13: -/* #line 280 "user/ansi_parser.rl" */ + case 14: +/* #line 287 "user/ansi_parser.rl" */ { leadchar = (*p); - {cs = 9;goto _again;} + {cs = 10;goto _again;} } break; - case 14: -/* #line 285 "user/ansi_parser.rl" */ + case 15: +/* #line 292 "user/ansi_parser.rl" */ { apars_handle_chs_designate(leadchar, (*p)); {cs = 1;goto _again;} } break; -/* #line 503 "user/ansi_parser.c" */ +/* #line 523 "user/ansi_parser.c" */ } } goto _again; @@ -517,7 +537,7 @@ _again: while ( __nacts-- > 0 ) { switch ( *__acts++ ) { case 0: -/* #line 178 "user/ansi_parser.rl" */ +/* #line 179 "user/ansi_parser.rl" */ { ansi_warn("Parser error."); apars_show_context(); @@ -527,7 +547,7 @@ _again: goto _again;} } break; -/* #line 531 "user/ansi_parser.c" */ +/* #line 551 "user/ansi_parser.c" */ } } } @@ -535,6 +555,6 @@ goto _again;} _out: {} } -/* #line 308 "user/ansi_parser.rl" */ +/* #line 315 "user/ansi_parser.rl" */ } diff --git a/user/ansi_parser.rl b/user/ansi_parser.rl index 67c3cba..c127f6c 100644 --- a/user/ansi_parser.rl +++ b/user/ansi_parser.rl @@ -76,6 +76,7 @@ ansi_parser(char newchar) { // The CSI code is built here static char leadchar; + static char interchar; // intermediate CSI char static int arg_ni; static int arg_cnt; static int arg[CSI_N_MAX]; @@ -195,6 +196,7 @@ ansi_parser(char newchar) action CSI_start { // Reset the CSI builder leadchar = NUL; + interchar = NUL; arg_ni = 0; arg_cnt = 0; @@ -224,14 +226,19 @@ ansi_parser(char newchar) arg_ni++; } + action CSI_intermed { + interchar = fc; + } + action CSI_end { - apars_handle_csi(leadchar, arg, arg_cnt, fc); + apars_handle_csi(leadchar, arg, arg_cnt, interchar, fc); fgoto main; } - CSI_body := ((32..47|60..64) @CSI_leading)? + #(32..47|60..64) + CSI_body := ([?>=] @CSI_leading)? ((digit @CSI_digit)* ';' @CSI_semi)* - (digit @CSI_digit)* (alpha|'`'|'@') @CSI_end $!errBadSeq; + (digit @CSI_digit)* ([ $*"+,)'&!\-] @CSI_intermed)? (alpha|[`@{}~|]) @CSI_end $!errBadSeq; # --- String commands --- diff --git a/user/apars_csi.c b/user/apars_csi.c index eeb4e35..cbac7de 100644 --- a/user/apars_csi.c +++ b/user/apars_csi.c @@ -3,19 +3,12 @@ // // Handle CSI sequences // CSI Pm -// (CSI = ESC [) // // Example of those are cursor manipulation sequences and SGR. // // For details, see: // http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Functions-using-CSI-_-ordered-by-the-final-character_s_ // -// Note: -// not all sequences listed in the xterm manual are implemented, notably sequences with the trailing symbol, -// graphic mode sequences, mouse reporting and complex multi-argument sequences that operate on regions. -// -// The screen size can be set using the xterm sequence: CSI Py ; Px t -// #include #include "apars_csi.h" @@ -29,86 +22,196 @@ #include "version.h" #include "syscfg.h" -// TODO simplify file - split to subroutines - -static void warn_bad_csi() -{ - ansi_noimpl_r("Unknown CSI"); - apars_show_context(); -} - +/** Struct passed to subroutines */ typedef struct { char lead; const int *n; int count; - char aug; // augmenting + char inter; char key; } CSI_Data; -static void do_csi_privattr(CSI_Data *opts); -static void do_csi_sgr(CSI_Data *opts); -static void do_csi_decreqtparm(CSI_Data *opts); +// Disambiguations +static inline void switch_csi_Plain(CSI_Data *opts); +static inline void switch_csi_NoLeadInterBang(CSI_Data *opts); +static inline void switch_csi_LeadGreater(CSI_Data *opts); +static inline void switch_csi_LeadQuest(CSI_Data *opts); +static inline void switch_csi_LeadEquals(CSI_Data *opts); + +// Subroutines +static inline void do_csi_sgr(CSI_Data *opts); +static inline void do_csi_decreqtparm(CSI_Data *opts); +static inline void do_csi_set_option(CSI_Data *opts); +static inline void do_csi_xterm_screen_cmd(CSI_Data *opts); +static inline void do_csi_set_private_option(CSI_Data *opts); + +/** + * Show warning and dump context for invalid CSI + */ +static void warn_bad_csi(void) +{ + ansi_noimpl_r("Unknown CSI"); + apars_show_context(); +} /** * Handle fully received CSI ANSI sequence - * @param leadchar - private range leading character, 0 if none + * + * @param leadchar - leading character * @param params - array of CSI_N_MAX ints holding the numeric arguments - * @param keychar - the char terminating the sequence + * @param count - actual amount of received numeric arguments + * @param keychar - intermediate character + * @param keychar - final character */ void ICACHE_FLASH_ATTR -apars_handle_csi(char leadchar, const int *params, int count, char keychar) +apars_handle_csi(char leadchar, const int *params, int count, char interchar, char keychar) { - CSI_Data opts = {leadchar, params, count, NUL, keychar}; - char buf[32]; - bool yn = false; // for ? l h - - int n1 = params[0]; - int n2 = params[1]; - int n3 = params[2]; - - // defaults - FIXME this may inadvertently affect some variants that should be left unchanged - switch (keychar) { - case 'A': // move - case 'a': - case 'e': - case 'B': - case 'C': - case 'D': - case 'b': - case 'E': - case 'F': + CSI_Data opts = {leadchar, params, count, interchar, keychar}; + + switch(leadchar) { + case '?': + switch_csi_LeadQuest(&opts); + break; + + case '>': + switch_csi_LeadGreater(&opts); + break; + + case '=': + switch_csi_LeadEquals(&opts); + break; + + case NUL: + // No leading character, switch by intermediate character + switch(interchar) { + case NUL: + switch_csi_Plain(&opts); + break; + + case '!': + switch_csi_NoLeadInterBang(&opts); + break; + +// case '\'': +// switch_csi_NoLeadInterApos(opts); +// break; + +// case '*': +// switch_csi_NoLeadInterStar(opts); +// break; + +// case '+': +// switch_csi_NoLeadInterPlus(opts); +// break; + +// case '"': +// switch_csi_NoLeadInterQuote(opts); +// break; + +// case '|': +// switch_csi_NoLeadInterDollar(opts); +// break; + +// case ' ': +// switch_csi_NoLeadInterSpace(opts); +// break; + +// case ',': +// switch_csi_NoLeadInterComma(opts); +// break; + +// case ')': +// switch_csi_NoLeadInterRparen(opts); +// break; + +// case '&': +// switch_csi_NoLeadInterAmpers(opts); +// break; + +// case '-': +// switch_csi_NoLeadInterDash(opts); +// break; + + default: + warn_bad_csi(); + } + break; + + default: + warn_bad_csi(); + } +} + + +/** + * CSI none Pm none key + * @param opts + */ +static inline void ICACHE_FLASH_ATTR +switch_csi_Plain(CSI_Data *opts) +{ + char resp_buf[20]; + int n1 = opts->n[0]; + int n2 = opts->n[1]; + + // fix arguments (default values etc) + switch (opts->key) { + // Single argument, 1-based + case 'A': // up + case 'e': // down (old) + case 'B': // down + case 'a': // right (old) + case 'C': // right + case 'D': // left + case 'E': // cursor next line + case 'F': // cursor prev line + case 'b': // repeat last char case 'G': // set X - case '`': - case 'S': // scrolling - case 'T': - case 'X': // clear in line + case '`': // set X (alias) case 'd': // set Y - case 'L': - case 'M': - case '@': - case 'P': - case 'I': - case 'Z': + case 'X': // clear in line + case 'S': // scroll up + case 'T': // scroll down + case 'L': // Insert lines + case 'M': // Delete lines + case '@': // Insert in line + case 'P': // Delete in line + case 'I': // Tab forward + case 'Z': // Tab backward if (n1 == 0) n1 = 1; break; - case 'H': + // Two arguments, 1-based + case 'H': // Absolute positioning case 'f': if (n1 == 0) n1 = 1; if (n2 == 0) n2 = 1; break; - case 'J': - case 'K': + // Erase modes 0,1,2 + case 'J': // Erase in screen + case 'K': // Erase in line if (n1 > 2) n1 = 0; break; + // No defaults + case 't': // Xterm window commands & reports + case 's': // Cursor save no attr + case 'r': // Set scrolling region + case 'u': // Cursor restore no attr + case 'h': // Option ON + case 'l': // Option OFF + case 'm': // SGR + case 'g': // Clear tabs + case 'n': // Queries 1 - device status + case 'c': // Queries 2 - primary DA + case 'x': // Queries 3 - DECREQTPARM default: // leave as is break; } - switch (keychar) { + switch (opts->key) { // CUU CUD CUF CUB case 'A': // Up screen_cursor_move(-n1, 0, false); @@ -161,63 +264,15 @@ apars_handle_csi(char leadchar, const int *params, int count, char keychar) // SU, SD - scroll up/down case 'S': - if (leadchar == NUL && count <= 1) { - screen_scroll_up(n1); - } - else { - // other: - // CSI ? Pi; Pa; Pv S (sixel) - warn_bad_csi(); - } + screen_scroll_up(n1); 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 - warn_bad_csi(); - } + screen_scroll_down(n1); 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); - apars_respond(buf); - break; - - case 21: // Report title - apars_respond("\033]L"); - apars_respond(termconf_scratch.title); - apars_respond("\033\\"); - break; - - case 24: // Set Height only - screen_resize(n2, termconf_scratch.width); - break; - - default: - ansi_noimpl("CSI %d t", n1); - break; - } - } - else { - // other: - // CSI > Ps; Ps t - // CSI Ps SP t, - warn_bad_csi(); - } + do_csi_xterm_screen_cmd(opts); break; // CUP,HVP - set position @@ -227,11 +282,6 @@ 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_noimpl("Selective erase"); - } - if (n1 == 0) { screen_clear(CLEAR_FROM_CURSOR); } else if (n1 == 1) { @@ -243,11 +293,6 @@ apars_handle_csi(char leadchar, const int *params, int count, char keychar) break; case 'K': // Erase lines - if (leadchar == '?') { - // TODO selective erase - ansi_noimpl("Selective erase"); - } - if (n1 == 0) { screen_clear_line(CLEAR_FROM_CURSOR); } else if (n1 == 1) { @@ -259,55 +304,25 @@ apars_handle_csi(char leadchar, const int *params, int count, char keychar) // SCP, RCP - save/restore position case 's': - if (leadchar == NUL && count == 0) { - screen_cursor_save(0); - } - else if (leadchar == '?') { - // Save private attributes (CSI ? Pm h/l) - ansi_noimpl("Save private attrs"); - apars_show_context(); - } - else { - // other: - // CSI ? Pm s - // CSI Pl; Pr s - warn_bad_csi(); - } + screen_cursor_save(0); break; case 'r': - if (leadchar == NUL && (count == 2 || count == 0)) { - screen_set_scrolling_region(n1-1, n2-1); - } - else if (leadchar == '?') { - // Restore private attributes (CSI ? Pm h/l) - ansi_noimpl("Restore private attrs"); - apars_show_context(); - } - else { - // other: - // CSI ? Pm r - // CSI Pt; Pl; Pb; Pr; Ps$ r - warn_bad_csi(); - } + screen_set_scrolling_region(n1-1, n2-1); break; case 'u': - if (leadchar == NUL && count == 0) { - screen_cursor_restore(0); - } - else { - warn_bad_csi(); - } + screen_cursor_restore(0); break; case 'h': // DEC feature enable case 'l': // DEC feature disable - do_csi_privattr(&opts); + // --- DEC standard attributes --- + do_csi_set_option(opts); break; case 'm': // SGR - set graphics rendition - do_csi_sgr(&opts); + do_csi_sgr(opts); break; case 'L': // Insert lines (shove down) @@ -343,39 +358,17 @@ apars_handle_csi(char leadchar, const int *params, int count, char keychar) screen_tab_forward(n1); break; - case 'p': - if (leadchar == '!') { // RIS - /* On real VT there are differences between soft and hard reset, we treat both equally */ - screen_reset(); - } - else { - warn_bad_csi(); - } - break; - case 'n': // Queries - if (leadchar == '>') { - // some xterm garbage - discard - // CSI > Ps n - ansi_noimpl("CSI > %d n", n1); - break; + if (n1 == 6) { + // Query cursor position + int x, y; + screen_cursor_get(&y, &x); + sprintf(resp_buf, "\033[%d;%dR", y + 1, x + 1); + apars_respond(resp_buf); } - - if (leadchar == NUL) { - if (n1 == 6) { - // Query cursor position - int x, y; - screen_cursor_get(&y, &x); - sprintf(buf, "\033[%d;%dR", y + 1, x + 1); - apars_respond(buf); - } - else if (n1 == 5) { - // Query device status - reply "Device is OK" - apars_respond("\033[0n"); - } - else { - warn_bad_csi(); - } + else if (n1 == 5) { + // Query device status - reply "Device is OK" + apars_respond("\033[0n"); } else { warn_bad_csi(); @@ -383,21 +376,12 @@ apars_handle_csi(char leadchar, const int *params, int count, char keychar) break; case 'c': // CSI-c - report capabilities - if (leadchar == NUL) { - apars_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); - apars_respond(buf); - } - else { - warn_bad_csi(); - } + // Primary device attributes + apars_respond("\033[?64;22;9c"); // pretend we're vt420 with national character sets and colors. break; case 'x': // DECREQTPARM -> DECREPTPARM - do_csi_decreqtparm(&opts); + do_csi_decreqtparm(opts); break; default: @@ -405,174 +389,113 @@ apars_handle_csi(char leadchar, const int *params, int count, char keychar) } } + /** - * CSI [?] Pm {h|l} - * @param opts + * CSI none Pm ! key */ -static void ICACHE_FLASH_ATTR do_csi_privattr(CSI_Data *opts) +static inline void ICACHE_FLASH_ATTR +switch_csi_NoLeadInterBang(CSI_Data *opts) { - bool yn = (opts->key == 'h'); + switch(opts->key) { + case 'p': + // RIS - CSI ! p + // On real VT there are differences between soft and hard reset, we treat both equally + screen_reset(); + break; - if (opts->lead == '?') { - // --- DEC private attributes --- - for (int i = 0; i < opts->count; i++) { - int n = opts->n[i]; - 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) { - // DECCOLM 132 column mode - not implemented due to RAM demands - ansi_noimpl("132col"); + default: + warn_bad_csi(); + } +} - // DECCOLM side effects as per - // https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/deccolm-cls.html - screen_clear(CLEAR_ALL); - screen_set_scrolling_region(0, 0); - screen_cursor_set(0, 0); - } - else if (n == 4) { - // Smooth scroll - not implemented - } - else if (n == 5) { - screen_set_reverse_video(yn); - } - else if (n == 6) { - screen_set_origin_mode(yn); - } - else if (n == 7) { - screen_wrap_enable(yn); - } - else if (n == 8) { - // Key auto-repeat - // We don't implement this currently, but it could be added - // - discard repeated keypress events between keydown and keyup. - ansi_noimpl("Auto-repeat toggle"); - } - else if (n == 9 || (n >= 1000 && n <= 1006) || n == 1015) { - // 9 - X10 tracking - // 1000 - X11 mouse - Send Mouse X & Y on button press and release. - // 1001 - Hilite mouse tracking - not impl - // 1002 - Cell Motion Mouse Tracking - // 1003 - All Motion Mouse Tracking - // 1004 - Send FocusIn/FocusOut events - // 1005 - Enable UTF-8 Mouse Mode - we implement this as an alias to X10 mode - // 1006 - SGR mouse mode - if (n == 9) mouse_tracking.mode = yn ? MTM_X10 : MTM_NONE; - else if (n == 1000) mouse_tracking.mode = yn ? MTM_NORMAL : MTM_NONE; - else if (n == 1002) mouse_tracking.mode = yn ? MTM_BUTTON_MOTION : MTM_NONE; - else if (n == 1003) mouse_tracking.mode = yn ? MTM_ANY_MOTION : MTM_NONE; - else if (n == 1004) mouse_tracking.focus_tracking = yn; - else if (n == 1005) mouse_tracking.encoding = yn ? MTE_UTF8 : MTE_SIMPLE; - else if (n == 1006) mouse_tracking.encoding = yn ? MTE_SGR : MTE_SIMPLE; - else if (n == 1015) mouse_tracking.encoding = yn ? MTE_URXVT : MTE_SIMPLE; - dbg("Mouse mode=%d, enc=%d, foctr=%d", mouse_tracking.mode, mouse_tracking.encoding, mouse_tracking.focus_tracking); - } - else if (n == 12) { - // TODO Cursor blink on/off - ansi_noimpl("Cursor blink toggle"); - } - else if (n == 40) { - // allow/disallow 80->132 mode - // not implemented because of RAM demands - ansi_noimpl("132col enable"); - } - else if (n == 45) { - // reverse wrap-around - ansi_noimpl("Reverse Wraparound"); - } - else if (n == 69) { - // horizontal margins - ansi_noimpl("Left/right margin"); - } - 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) { - // 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_noimpl("FN key emul type"); - } - else if (n == 2004) { - // Bracketed paste mode - // Discard, we don't implement this - } - else if (n == 25) { - screen_set_cursor_visible(yn); - } - else if (n == 800) { // ESPTerm: Toggle display of buttons - termconf_scratch.show_buttons = yn; - screen_notifyChange(CHANGE_CONTENT); // this info is included in the screen preamble - } - else if (n == 801) { // ESPTerm: Toggle display of config links - termconf_scratch.show_config_links = yn; - screen_notifyChange(CHANGE_CONTENT); // this info is included in the screen preamble - } - else { - ansi_noimpl("CSI ? %d %c", n, opts->key); - } - } + +/** + * CSI > Pm inter key + */ +static inline void ICACHE_FLASH_ATTR +switch_csi_LeadGreater(CSI_Data *opts) +{ + char resp_buf[20]; + switch(opts->key) { + case 'c': // CSI > c - secondary device attributes query + // 41 - we're "VT400", FV wers, 0 - ROM cartridge number + sprintf(resp_buf, "\033[>41;%d;0c", FIRMWARE_VERSION_NUM); + apars_respond(resp_buf); + break; + + default: + warn_bad_csi(); } - else { - // --- DEC standard attributes --- - for (int i = 0; i < opts->count; i++) { - int n = opts->n[i]; +} - if (n == 4) { - screen_set_insert_mode(yn); - } - else if (n == 12) { - // SRM is inverted, according to vt510 manual - termconf_scratch.loopback = !yn; - } - else if (n == 20) { - screen_set_newline_mode(yn); - } - else { - ansi_noimpl("CSI %d %c", n, opts->key); - } - } + +/** + * CSI = Pm inter key + */ +static inline void ICACHE_FLASH_ATTR +switch_csi_LeadEquals(CSI_Data *opts) +{ + char resp_buf[20]; + u8 mac[6]; + switch(opts->key) { + case 'c': // CSI = c - tertiary device attributes query + // report our unique ID number + wifi_get_macaddr(SOFTAP_IF, mac); + sprintf(resp_buf, "\033P!|%02X%02X%02X\033\\", mac[3], mac[4], mac[5]); + apars_respond(resp_buf); + break; + + default: + warn_bad_csi(); + } +} + +/** + * CSI ? Pm inter key + */ +static inline void ICACHE_FLASH_ATTR +switch_csi_LeadQuest(CSI_Data *opts) +{ + switch(opts->key) { + case 's': + // Save private attributes + ansi_noimpl("Save private attrs"); + apars_show_context(); // TODO priv attr s/r + break; + + case 'r': + // Restore private attributes + ansi_noimpl("Restore private attrs"); + apars_show_context(); // TODO priv attr s/r + break; + + case 'J': // Erase screen selectively + // TODO selective erase + ansi_noimpl("Selective screen erase"); + break; + + case 'K': // Erase line selectively + // TODO selective erase + ansi_noimpl("Selective line erase"); + break; + + case 'l': + case 'h': + do_csi_set_private_option(opts); + break; + + default: + warn_bad_csi(); } } + /** - * CSI [ Pm m + * CSI Pm m * @param opts */ -static void ICACHE_FLASH_ATTR do_csi_sgr(CSI_Data *opts) +static inline void ICACHE_FLASH_ATTR +do_csi_sgr(CSI_Data *opts) { int count = opts->count; @@ -580,12 +503,6 @@ static void ICACHE_FLASH_ATTR do_csi_sgr(CSI_Data *opts) count = 1; // this makes it work as 0 (reset) } - if (opts->lead != NUL) { - // some xterm garbage - discard - // CSI > Ps; Ps m - return; - } - // iterate arguments for (int i = 0; i < count; i++) { int n = opts->n[i]; @@ -623,8 +540,217 @@ static void ICACHE_FLASH_ATTR do_csi_sgr(CSI_Data *opts) } -// data tables for the DECREPTPARM command response +/** + * CSI Pm h or l + * @param opts + */ +static inline void ICACHE_FLASH_ATTR +do_csi_set_option(CSI_Data *opts) +{ + bool yn = (opts->key == 'h'); + + for (int i = 0; i < opts->count; i++) { + int n = opts->n[i]; + + if (n == 4) { + screen_set_insert_mode(yn); + } + else if (n == 12) { + // SRM is inverted, according to vt510 manual + termconf_scratch.loopback = !yn; + } + else if (n == 20) { + screen_set_newline_mode(yn); + } + else { + ansi_noimpl("OPTION %d", n); + } + } +} + + +/** + * CSI ? Pm h or l + * @param opts + */ +static inline void ICACHE_FLASH_ATTR +do_csi_set_private_option(CSI_Data *opts) +{ + bool yn = (opts->key == 'h'); + + // --- DEC private attributes --- + for (int i = 0; i < opts->count; i++) { + int n = opts->n[i]; + 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) { + // DECCOLM 132 column mode - not implemented due to RAM demands + ansi_noimpl("132col"); + + // DECCOLM side effects as per + // https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/deccolm-cls.html + screen_clear(CLEAR_ALL); + screen_set_scrolling_region(0, 0); + screen_cursor_set(0, 0); + } + else if (n == 4) { + // Smooth scroll - not implemented + } + else if (n == 5) { + screen_set_reverse_video(yn); + } + else if (n == 6) { + screen_set_origin_mode(yn); + } + else if (n == 7) { + screen_wrap_enable(yn); + } + else if (n == 8) { + // Key auto-repeat + // We don't implement this currently, but it could be added + // - discard repeated keypress events between keydown and keyup. + ansi_noimpl("Auto-repeat toggle"); + } + else if (n == 9 || (n >= 1000 && n <= 1006) || n == 1015) { + // 9 - X10 tracking + // 1000 - X11 mouse - Send Mouse X & Y on button press and release. + // 1001 - Hilite mouse tracking - not impl + // 1002 - Cell Motion Mouse Tracking + // 1003 - All Motion Mouse Tracking + // 1004 - Send FocusIn/FocusOut events + // 1005 - Enable UTF-8 Mouse Mode - we implement this as an alias to X10 mode + // 1006 - SGR mouse mode + if (n == 9) mouse_tracking.mode = yn ? MTM_X10 : MTM_NONE; + else if (n == 1000) mouse_tracking.mode = yn ? MTM_NORMAL : MTM_NONE; + else if (n == 1002) mouse_tracking.mode = yn ? MTM_BUTTON_MOTION : MTM_NONE; + else if (n == 1003) mouse_tracking.mode = yn ? MTM_ANY_MOTION : MTM_NONE; + else if (n == 1004) mouse_tracking.focus_tracking = yn; + else if (n == 1005) mouse_tracking.encoding = yn ? MTE_UTF8 : MTE_SIMPLE; + else if (n == 1006) mouse_tracking.encoding = yn ? MTE_SGR : MTE_SIMPLE; + else if (n == 1015) mouse_tracking.encoding = yn ? MTE_URXVT : MTE_SIMPLE; + + dbg("Mouse mode=%d, enc=%d, foctr=%d", + mouse_tracking.mode, + mouse_tracking.encoding, + mouse_tracking.focus_tracking); + } + else if (n == 12) { + // TODO Cursor blink on/off + ansi_noimpl("Cursor blink toggle"); + } + else if (n == 40) { + // allow/disallow 80->132 mode + // not implemented because of RAM demands + ansi_noimpl("132col enable"); + } + else if (n == 45) { + // reverse wrap-around + ansi_noimpl("Reverse Wraparound"); + } + else if (n == 69) { + // horizontal margins + ansi_noimpl("Left/right margin"); + } + 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) { + // 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 == 2004) { + // Bracketed paste mode + ansi_noimpl("Bracketed paste"); + } + else if (n == 25) { + screen_set_cursor_visible(yn); + } + else if (n == 800) { // ESPTerm: Toggle display of buttons + termconf_scratch.show_buttons = yn; + screen_notifyChange(CHANGE_CONTENT); // this info is included in the screen preamble + } + else if (n == 801) { // ESPTerm: Toggle display of config links + termconf_scratch.show_config_links = yn; + screen_notifyChange(CHANGE_CONTENT); // this info is included in the screen preamble + } + else { + ansi_noimpl("?OPTION %d", n); + } + } +} + + +/** + * CSI Ps ; Ps ; Ps t + * @param opts + */ +static inline void ICACHE_FLASH_ATTR +do_csi_xterm_screen_cmd(CSI_Data *opts) +{ + char resp_buf[20]; + switch (opts->n[0]) { + case 8: // set size + screen_resize(opts->n[1], opts->n[2]); + break; + case 18: // Report the size of the text area in characters. + case 19: // Report the size of the screen in characters. + sprintf(resp_buf, "\033[8;%d;%dt", termconf_scratch.height, termconf_scratch.width); + apars_respond(resp_buf); + break; + + case 20: // Report icon + case 21: // Report title + apars_respond("\033]l"); + apars_respond(termconf_scratch.title); + apars_respond("\033\\"); + break; + + case 22: + ansi_noimpl("Push title"); + break; + case 23: + ansi_noimpl("Pop title"); + break; + + case 24: // Set Height only + screen_resize(opts->n[1], termconf_scratch.width); + break; + + default: + ansi_noimpl("Xterm win report %d", opts->n[0]); + break; + } +} + + +// 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"}, @@ -655,10 +781,11 @@ static const struct DECREPTPARM_baud DECREPTPARM_baud_arr[] = { }; /** - * CSI [ Ps x + * CSI Ps x * @param opts */ -static void ICACHE_FLASH_ATTR do_csi_decreqtparm(CSI_Data *opts) +static inline void ICACHE_FLASH_ATTR +do_csi_decreqtparm(CSI_Data *opts) { const int n1 = opts->n[0]; diff --git a/user/apars_csi.h b/user/apars_csi.h index cbd0a72..671fe18 100644 --- a/user/apars_csi.h +++ b/user/apars_csi.h @@ -5,6 +5,6 @@ #ifndef ESP_VT100_FIRMWARE_APARS_CSI_H #define ESP_VT100_FIRMWARE_APARS_CSI_H -void apars_handle_csi(char leadchar, const int *params, int count, char keychar); +void apars_handle_csi(char leadchar, const int *params, int count, char interchar, char keychar); #endif //ESP_VT100_FIRMWARE_APARS_CSI_H diff --git a/user/screen.c b/user/screen.c index e50a2db..7848497 100644 --- a/user/screen.c +++ b/user/screen.c @@ -56,6 +56,9 @@ static struct { bool newline_mode; //!< LNM - CR automatically sends LF bool reverse; //!< DECSCNM - Reverse video + bool insert_mode; //!< IRM - Insert mode (move rest of the line to the right) + bool cursor_visible; //!< DECTCEM - Cursor visible + // Vertical margin bounds (inclusive start/end of scrolling region) int vm0; int vm1; @@ -64,6 +67,9 @@ static struct { #define R0 scr.vm0 #define R1 scr.vm1 #define RH (scr.vm1 - scr.vm0 + 1) +// horizontal edges - will be useful if horizontal margin is implemented +#define C0 0 +#define C1 (W-1) typedef struct { /* Cursor position */ @@ -84,12 +90,9 @@ typedef struct { char charset0; char charset1; - /** DEC private modes */ - bool wraparound; //!< DECAWM - Wrapping when EOL - bool origin_mode; //!< DECOM - absolute positioning is relative to vertical margins - - bool insert_mode; //!< IRM - Insert mode (move rest of the line to the right) - bool visible; //!< DECTCEM - Cursor visible + /** Options saved with cursor */ + bool auto_wrap; //!< DECAWM - Wrapping when EOL + bool origin_mode; //!< DECOM - absolute positioning is relative to vertical margins } CursorTypeDef; /** @@ -246,18 +249,26 @@ cursor_reset(void) cursor.x = 0; cursor.y = 0; cursor.hanging = false; - cursor.visible = true; - cursor.insert_mode = false; - cursor.origin_mode = false; cursor.charsetN = 0; cursor.charset0 = CS_B_USASCII; cursor.charset1 = CS_0_DEC_SUPPLEMENTAL; - cursor.wraparound = true; screen_reset_sgr(); } +/** + * Reset the cursor position & colors + */ +static void ICACHE_FLASH_ATTR +decopt_reset(void) +{ + scr.cursor_visible = true; + scr.insert_mode = false; + cursor.origin_mode = false; + cursor.auto_wrap = true; +} + /** * Reset the cursor (\e[0m) */ @@ -279,6 +290,7 @@ screen_reset(void) NOTIFY_LOCK(); cursor_reset(); + decopt_reset(); scr.numpad_alt_mode = false; scr.cursors_alt_mode = false; @@ -774,6 +786,10 @@ screen_cursor_get(int *y, int *x) { *x = cursor.x; *y = cursor.y; + + if (cursor.origin_mode) { + *y -= R0; + } } /** @@ -914,7 +930,7 @@ void ICACHE_FLASH_ATTR screen_set_cursor_visible(bool visible) { NOTIFY_LOCK(); - cursor.visible = visible; + scr.cursor_visible = visible; NOTIFY_DONE(); } @@ -924,7 +940,7 @@ screen_set_cursor_visible(bool visible) void ICACHE_FLASH_ATTR screen_wrap_enable(bool enable) { - cursor.wraparound = enable; + cursor.auto_wrap = enable; } /** @@ -964,19 +980,6 @@ screen_set_sgr_inverse(bool ena) cursor.inverse = ena; } -/** - * Check if coords are in range - used for verifying mouse clicks - * - * @param y - * @param x - * @return OK - */ -bool ICACHE_FLASH_ATTR -screen_isCoordValid(int y, int x) -{ - return x >= 0 && y >= 0 && x < W && y < H; -} - void ICACHE_FLASH_ATTR screen_set_charset_n(int Gx) { @@ -994,7 +997,7 @@ screen_set_charset(int Gx, char charset) void ICACHE_FLASH_ATTR screen_set_insert_mode(bool insert) { - cursor.insert_mode = insert; + scr.insert_mode = insert; } void ICACHE_FLASH_ATTR @@ -1099,7 +1102,7 @@ screen_putchar(const char *ch) if (cursor.hanging) { // perform the scheduled wrap if hanging // if auto-wrap = off, it overwrites the last char - if (cursor.wraparound) { + if (cursor.auto_wrap) { cursor.x = 0; cursor.y++; // Y wrap @@ -1116,7 +1119,7 @@ screen_putchar(const char *ch) Cell *c = &screen[cursor.x + cursor.y * W]; // move the rest of the line if we're in Insert Mode - if (cursor.x < W-1 && cursor.insert_mode) screen_insert_characters(1); + if (cursor.x < W-1 && scr.insert_mode) screen_insert_characters(1); if (ch[1] == 0 && ch[0] <= 0x7f) { // we have len=1 and ASCII @@ -1289,7 +1292,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data) encode2B((u16) cursor.y, &w3); encode2B((u16) cursor.x, &w4); encode2B((u16) ( - (cursor.visible ? 1<<0 : 0) | + (scr.cursor_visible ? 1<<0 : 0) | (cursor.hanging ? 1<<1 : 0) | (scr.cursors_alt_mode ? 1<<2 : 0) | (scr.numpad_alt_mode ? 1<<3 : 0) | diff --git a/user/screen.h b/user/screen.h index 5dc6755..a87a113 100644 --- a/user/screen.h +++ b/user/screen.h @@ -271,8 +271,6 @@ 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); diff --git a/user/serial.c b/user/serial.c index dce1f85..e27d8c8 100644 --- a/user/serial.c +++ b/user/serial.c @@ -40,10 +40,10 @@ static void buf_pop(void *unused) } } -LOCAL void my_putc(char c) -{ - UART_WriteCharCRLF(UART1, (u8) c, 10); -} +//LOCAL void my_putc(char c) +//{ +// UART_WriteCharCRLF(UART1, (u8) c, 10); +//} /** * Init the serial ports diff --git a/user/version.h b/user/version.h index f0e669b..ab1e13e 100644 --- a/user/version.h +++ b/user/version.h @@ -10,7 +10,7 @@ #define FW_V_PATCH 0 #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 FIRMWARE_VERSION_NUM (FW_V_MAJOR*1000 + FW_V_MINOR*10 + FW_V_PATCH) // this is used in ID queries #define TERMINAL_GITHUB_REPO "https://github.com/MightyPork/ESPTerm" #endif //ESP_VT100_FIRMWARE_VERSION_H