Fixed a couple bugs and cleaned up the parser callbacks file for CSI

pull/111/merge
Ondřej Hruška 7 years ago
parent a78830f805
commit 116201e232
  1. 210
      user/ansi_parser.c
  2. 13
      user/ansi_parser.rl
  3. 845
      user/apars_csi.c
  4. 2
      user/apars_csi.h
  5. 61
      user/screen.c
  6. 2
      user/screen.h
  7. 8
      user/serial.c
  8. 2
      user/version.h

@ -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" */
}

@ -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 ---

@ -3,19 +3,12 @@
//
// Handle CSI sequences
// CSI <symbol?> Pm <symbol?> <char>
// (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 <esp8266.h>
#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];

@ -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

@ -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) |

@ -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);

@ -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

@ -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

Loading…
Cancel
Save