Stubs for scrolling region and other functions, implemented inverse video, improved parse (hardeing against xterm travesty), optimize tabs, DECREPTPARM

pull/111/merge
Ondřej Hruška 7 years ago
parent 5995988a9b
commit 59f26adf68
  1. 20
      html_orig/js/app.js
  2. 20
      html_orig/jssrc/term.js
  3. 4
      user/ansi_parser.c
  4. 4
      user/ansi_parser.rl
  5. 350
      user/ansi_parser_callbacks.c
  6. 16
      user/ascii.h
  7. 116
      user/screen.c
  8. 163
      user/screen.h
  9. 7
      user/user_main.h

@ -1693,8 +1693,8 @@ var Screen = (function () {
cell = { cell = {
t: ' ', t: ' ',
fg: cursor.fg, fg: 7,
bg: cursor.bg, bg: 0, // the colors will be replaced immediately as we receive data (user won't see this)
attrs: 0, attrs: 0,
e: e, e: e,
x: i % W, x: i % W,
@ -1768,20 +1768,18 @@ var Screen = (function () {
// Attributes // Attributes
num = parse2B(str, i); i += 2; // fg bg bold hidden num = parse2B(str, i); i += 2; // fg bg bold hidden
cursor.fg = num & 0x0F; cursor.hidden = !(num & 0x0001);
cursor.bg = (num & 0xF0) >> 4; cursor.hanging = !!(num & 0x0002);
cursor.hidden = !(num & 0x100);
cursor.hanging = !!(num & 0x200);
// console.log("Attributes word ",num.toString(16)+'h'); // console.log("Attributes word ",num.toString(16)+'h');
Input.setAlts( Input.setAlts(
!!(num & 0x400), // cu !!(num & 0x0004), // cu
!!(num & 0x800), // np !!(num & 0x0008), // np
!!(num & 0x1000) // fn !!(num & 0x0010) // fn
); );
fg = cursor.fg; fg = 7;
bg = cursor.bg; bg = 0;
attrs = 0; attrs = 0;
// Here come the content // Here come the content

@ -136,8 +136,8 @@ var Screen = (function () {
cell = { cell = {
t: ' ', t: ' ',
fg: cursor.fg, fg: 7,
bg: cursor.bg, bg: 0, // the colors will be replaced immediately as we receive data (user won't see this)
attrs: 0, attrs: 0,
e: e, e: e,
x: i % W, x: i % W,
@ -211,20 +211,18 @@ var Screen = (function () {
// Attributes // Attributes
num = parse2B(str, i); i += 2; // fg bg bold hidden num = parse2B(str, i); i += 2; // fg bg bold hidden
cursor.fg = num & 0x0F; cursor.hidden = !(num & 0x0001);
cursor.bg = (num & 0xF0) >> 4; cursor.hanging = !!(num & 0x0002);
cursor.hidden = !(num & 0x100);
cursor.hanging = !!(num & 0x200);
// console.log("Attributes word ",num.toString(16)+'h'); // console.log("Attributes word ",num.toString(16)+'h');
Input.setAlts( Input.setAlts(
!!(num & 0x400), // cu !!(num & 0x0004), // cu
!!(num & 0x800), // np !!(num & 0x0008), // np
!!(num & 0x1000) // fn !!(num & 0x0010) // fn
); );
fg = cursor.fg; fg = 7;
bg = cursor.bg; bg = 0;
attrs = 0; attrs = 0;
// Here come the content // Here come the content

@ -155,7 +155,7 @@ ansi_parser(char newchar)
return; return;
case TAB: case TAB:
screen_tab_forward(); screen_tab_forward(1);
return; return;
// Select G0 or G1 // Select G0 or G1
@ -405,7 +405,7 @@ execFuncs:
/* #line 202 "user/ansi_parser.rl" */ /* #line 202 "user/ansi_parser.rl" */
{ {
// Reset the CSI builder // Reset the CSI builder
leadchar = 0; leadchar = NUL;
arg_ni = 0; arg_ni = 0;
arg_cnt = 0; arg_cnt = 0;

@ -124,7 +124,7 @@ ansi_parser(char newchar)
return; return;
case TAB: case TAB:
screen_tab_forward(); screen_tab_forward(1);
return; return;
// Select G0 or G1 // Select G0 or G1
@ -201,7 +201,7 @@ ansi_parser(char newchar)
action CSI_start { action CSI_start {
// Reset the CSI builder // Reset the CSI builder
leadchar = 0; leadchar = NUL;
arg_ni = 0; arg_ni = 0;
arg_cnt = 0; arg_cnt = 0;

@ -5,12 +5,16 @@
*/ */
#include <esp8266.h> #include <esp8266.h>
#include <helpers.h>
#include "ansi_parser_callbacks.h" #include "ansi_parser_callbacks.h"
#include "screen.h" #include "screen.h"
#include "ansi_parser.h" #include "ansi_parser.h"
#include "uart_driver.h" #include "uart_driver.h"
#include "sgr.h" #include "sgr.h"
#include "cgi_sockets.h" #include "cgi_sockets.h"
#include "ascii.h"
#include "user_main.h"
#include "syscfg.h"
static char utf_collect[4]; static char utf_collect[4];
static int utf_i = 0; static int utf_i = 0;
@ -131,6 +135,37 @@ apars_handle_bel(void)
send_beep(); 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 * Handle fully received CSI ANSI sequence
* @param leadchar - private range leading character, 0 if none * @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 n1 = params[0];
int n2 = params[1]; int n2 = params[1];
int n3 = params[2]; int n3 = params[2];
static char buf[20]; char buf[32];
bool yn = 0; // for ? l h bool yn = false; // for ? l h
// defaults // defaults - FIXME this may inadvertently affect some variants that should be left unchanged
switch (keychar) { switch (keychar) {
case 'A': // move case 'A': // move
case 'B': case 'B':
@ -166,6 +201,7 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar)
case 'P': case 'P':
case 'I': case 'I':
case 'Z': case 'Z':
case 'b':
if (n1 == 0) n1 = 1; if (n1 == 0) n1 = 1;
break; break;
@ -215,6 +251,11 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar)
screen_cursor_set_x(0); screen_cursor_set_x(0);
break; break;
case 'b':
// TODO repeat preceding graphic character n1 times
ansi_warn("NOIMPL: Repeat char");
return;
// Set X // Set X
case 'G': case 'G':
case '`': // alternate code case '`': // alternate code
@ -232,8 +273,66 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar)
break; // 1-based break; // 1-based
// SU, SD - scroll up/down // SU, SD - scroll up/down
case 'S': screen_scroll_up(n1); break; case 'S':
case 'T': screen_scroll_down(n1); break; 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 // CUP,HVP - set position
case 'H': case 'H':
@ -242,6 +341,11 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar)
break; // 1-based break; // 1-based
case 'J': // Erase screen case 'J': // Erase screen
if (leadchar == '?') {
// TODO selective erase
ansi_warn("NOIMPL: Selective erase");
}
if (n1 == 0) { if (n1 == 0) {
screen_clear(CLEAR_FROM_CURSOR); screen_clear(CLEAR_FROM_CURSOR);
} else if (n1 == 1) { } else if (n1 == 1) {
@ -253,6 +357,11 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar)
break; break;
case 'K': // Erase lines case 'K': // Erase lines
if (leadchar == '?') {
// TODO selective erase
ansi_warn("NOIMPL: Selective erase");
}
if (n1 == 0) { if (n1 == 0) {
screen_clear_line(CLEAR_FROM_CURSOR); screen_clear_line(CLEAR_FROM_CURSOR);
} else if (n1 == 1) { } else if (n1 == 1) {
@ -263,10 +372,37 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar)
break; break;
// SCP, RCP - save/restore position // SCP, RCP - save/restore position
case 's': screen_cursor_save(0); break; case 's':
case 'u': screen_cursor_restore(0); break; 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 case 'n': // Queries
if (leadchar == '>') {
// some xterm garbage - discard
// CSI > Ps n
ansi_warn("NOIMPL: CSI > %d n", n1);
break;
}
if (n1 == 6) { if (n1 == 6) {
// Query cursor position // Query cursor position
int x, y; int x, y;
@ -279,29 +415,47 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar)
respond("\033[0n"); respond("\033[0n");
} }
else { else {
ansi_warn("NOIMPL: CSI %d n", n1); ansi_warn("NOIMPL: CSI");
apars_handle_badseq();
} }
break; break;
case 'h': // DEC feature enable case 'h': // DEC feature enable
yn = 1; yn = 1;
case 'l': // DEC feature disable case 'l': // DEC feature disable
// yn is 0 by default
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
int n = params[i]; int n = params[i];
if (leadchar == '?') { if (leadchar == '?') {
if (n == 1) { if (n == 1) {
screen_set_cursors_alt_mode(yn); 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) { else if (n == 6) {
// TODO origin mode (scrolling region) screen_set_origin_mode(yn);
} }
else if (n == 7) { else if (n == 7) {
screen_wrap_enable(yn); screen_wrap_enable(yn);
} }
else if (n == 8) { else if (n == 8) {
// Key auto-repeat // TODO Key auto-repeat
// We don't implement this currently, but it could be added // We don't implement this currently, but it could be added
// - discard repeated keypress events between keydown and keyup. // - discard repeated keypress events between keydown and keyup.
ansi_warn("NOIMPL: Auto-repeat toggle");
} }
else if (n == 9 || (n >= 1000 && n <= 1006)) { else if (n == 9 || (n >= 1000 && n <= 1006)) {
// TODO mouse // TODO mouse
@ -312,20 +466,60 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar)
// 1004 - Send FocusIn/FocusOut events // 1004 - Send FocusIn/FocusOut events
// 1005 - Enable UTF-8 Mouse Mode // 1005 - Enable UTF-8 Mouse Mode
// 1006 - SGR mouse mode // 1006 - SGR mouse mode
ansi_warn("NOIMPL: Mouse tracking");
} }
else if (n == 12) { else if (n == 12) {
// TODO Cursor blink on/off // 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) { else if (n == 1049) {
// XTERM: optionally switch to/from alternate screen // save/restore cursor and screen and clear it
// We can't implement this because of limited RAM size 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) { else if (n == 2004) {
// Bracketed paste mode // Bracketed paste mode
// Discard, we don't implement this // Discard, we don't implement this
} }
else if (n == 25) { else if (n == 25) {
screen_cursor_visible(yn); screen_set_cursor_visible(yn);
} }
else { else {
ansi_warn("NOIMPL: CSI ? %d %c", n, keychar); 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) count = 1; // this makes it work as 0 (reset)
} }
if (leadchar == '>') {
// some xterm garbage - discard
// CSI > Ps; Ps m
break;
}
// iterate arguments // iterate arguments
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
int n = params[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_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 else if (n == SGR_BG_DEFAULT) screen_set_bg(termconf_scratch.default_bg); // default bg
// -- set attr -- // -- set attr --
else if (n == SGR_BOLD) screen_attr_enable(ATTR_BOLD); else if (n == SGR_BOLD) screen_set_sgr(ATTR_BOLD, 1);
else if (n == SGR_FAINT) screen_attr_enable(ATTR_FAINT); else if (n == SGR_FAINT) screen_set_sgr(ATTR_FAINT, 1);
else if (n == SGR_ITALIC) screen_attr_enable(ATTR_ITALIC); else if (n == SGR_ITALIC) screen_set_sgr(ATTR_ITALIC, 1);
else if (n == SGR_UNDERLINE) screen_attr_enable(ATTR_UNDERLINE); else if (n == SGR_UNDERLINE) screen_set_sgr(ATTR_UNDERLINE, 1);
else if (n == SGR_BLINK || n == SGR_BLINK_FAST) screen_attr_enable(ATTR_BLINK); // 6 - rapid blink, not supported else if (n == SGR_BLINK || n == SGR_BLINK_FAST) screen_set_sgr(ATTR_BLINK, 1); // 6 - rapid blink, not supported
else if (n == SGR_INVERSE) screen_inverse_enable(true); else if (n == SGR_STRIKE) screen_set_sgr(ATTR_STRIKE, 1);
else if (n == SGR_STRIKE) screen_attr_enable(ATTR_STRIKE); else if (n == SGR_FRAKTUR) screen_set_sgr(ATTR_FRAKTUR, 1);
else if (n == SGR_FRAKTUR) screen_attr_enable(ATTR_FRAKTUR); else if (n == SGR_INVERSE) screen_set_sgr_inverse(1);
// -- clear attr -- // -- clear attr --
else if (n == SGR_OFF(SGR_BOLD)) screen_attr_disable(ATTR_BOLD); else if (n == SGR_OFF(SGR_BOLD)) screen_set_sgr(ATTR_BOLD, 0);
else if (n == SGR_OFF(SGR_FAINT)) screen_attr_disable(ATTR_FAINT); else if (n == SGR_OFF(SGR_FAINT)) screen_set_sgr(ATTR_FAINT, 0);
else if (n == SGR_OFF(SGR_ITALIC)) screen_attr_disable(ATTR_ITALIC|ATTR_FRAKTUR); 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_attr_disable(ATTR_UNDERLINE); else if (n == SGR_OFF(SGR_UNDERLINE)) screen_set_sgr(ATTR_UNDERLINE, 0);
else if (n == SGR_OFF(SGR_BLINK)) screen_attr_disable(ATTR_BLINK); else if (n == SGR_OFF(SGR_BLINK)) screen_set_sgr(ATTR_BLINK, 0);
else if (n == SGR_OFF(SGR_INVERSE)) screen_inverse_enable(false); else if (n == SGR_OFF(SGR_STRIKE)) screen_set_sgr(ATTR_STRIKE, 0);
else if (n == SGR_OFF(SGR_STRIKE)) screen_attr_disable(ATTR_STRIKE); else if (n == SGR_OFF(SGR_INVERSE)) screen_set_sgr_inverse(0);
// -- AIX bright colors -- // -- 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_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 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; 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) case 'L': // Insert lines (shove down)
screen_insert_lines(n1); screen_insert_lines(n1);
break; break;
@ -429,7 +603,16 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar)
break; break;
case 'r': 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; break;
case 'g': // Clear tabs case 'g': // Clear tabs
@ -441,19 +624,55 @@ apars_handle_CSI(char leadchar, const int *params, int count, char keychar)
break; break;
case 'Z': // Tab backward case 'Z': // Tab backward
for(; n1 > 0; n1--) { screen_tab_reverse(n1);
screen_tab_reverse();
}
break; break;
case 'I': // Tab forward case 'I': // Tab forward
for(; n1 > 0; n1--) { screen_tab_forward(n1);
screen_tab_forward();
}
break; break;
case 'c': // CSI-c - report capabilities 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; break;
default: 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) void ICACHE_FLASH_ATTR apars_handle_hashCode(char c)
{ {
switch(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': case '8':
screen_fill_with_E(); screen_fill_with_E();
break; break;

@ -13,13 +13,13 @@ enum ASCII_CODES {
EOT = 4, EOT = 4,
ENQ = 5, ENQ = 5,
ACK = 6, ACK = 6,
BEL = 7, BEL = 7, /* \a */
BS = 8, BS = 8, /* \b */
TAB = 9, TAB = 9, /* \t */
LF = 10, LF = 10, /* \n */
VT = 11, VT = 11, /* \v */
FF = 12, FF = 12, /* \f */
CR = 13, CR = 13, /* \r */
SO = 14, SO = 14,
SI = 15, SI = 15,
DLE = 16, DLE = 16,
@ -33,7 +33,7 @@ enum ASCII_CODES {
CAN = 24, CAN = 24,
EM = 25, EM = 25,
SUB = 26, SUB = 26,
ESC = 27, ESC = 27, /* \033, \x1b, \e */
FS = 28, FS = 28,
GS = 29, GS = 29,
RS = 30, RS = 30,

@ -3,6 +3,7 @@
#include "screen.h" #include "screen.h"
#include "persist.h" #include "persist.h"
#include "sgr.h" #include "sgr.h"
#include "ascii.h"
TerminalConfigBundle * const termconf = &persist.current.termconf; TerminalConfigBundle * const termconf = &persist.current.termconf;
TerminalConfigBundle termconf_scratch; TerminalConfigBundle termconf_scratch;
@ -48,6 +49,7 @@ static struct {
bool cursors_alt_mode; //!< Application mode for cursor keys bool cursors_alt_mode; //!< Application mode for cursor keys
bool newline_mode; bool newline_mode;
bool reverse;
char charset0; char charset0;
char charset1; char charset1;
@ -205,6 +207,7 @@ screen_reset(void)
scr.numpad_alt_mode = false; scr.numpad_alt_mode = false;
scr.cursors_alt_mode = false; scr.cursors_alt_mode = false;
scr.newline_mode = false; scr.newline_mode = false;
scr.reverse = false;
scr.charset0 = 'B'; scr.charset0 = 'B';
scr.charset1 = '0'; scr.charset1 = '0';
@ -216,6 +219,18 @@ screen_reset(void)
NOTIFY_DONE(); 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 //endregion
//region --- Tab stops --- //region --- Tab stops ---
@ -305,28 +320,33 @@ prev_tab_stop(void)
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_tab_forward(void) screen_tab_forward(int count)
{ {
NOTIFY_LOCK(); NOTIFY_LOCK();
for (; count > 0; count--) {
int tab = next_tab_stop(); int tab = next_tab_stop();
if (tab != -1) { if (tab != -1) {
cursor.x = tab; cursor.x = tab;
} else { }
else {
cursor.x = W - 1; cursor.x = W - 1;
} }
}
NOTIFY_DONE(); NOTIFY_DONE();
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_tab_reverse(void) screen_tab_reverse(int count)
{ {
NOTIFY_LOCK(); NOTIFY_LOCK();
for (; count > 0; count--) {
int tab = prev_tab_stop(); int tab = prev_tab_stop();
if (tab != -1) { if (tab != -1) {
cursor.x = tab; cursor.x = tab;
} else { } else {
cursor.x = 0; cursor.x = 0;
} }
}
NOTIFY_DONE(); NOTIFY_DONE();
} }
@ -600,6 +620,14 @@ screen_scroll_down(unsigned int lines)
done: done:
NOTIFY_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 //endregion
//region --- Cursor manipulation --- //region --- Cursor manipulation ---
@ -726,7 +754,7 @@ screen_cursor_restore(bool withAttrs)
* Enable cursor display * Enable cursor display
*/ */
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_cursor_visible(bool visible) screen_set_cursor_visible(bool visible)
{ {
NOTIFY_LOCK(); NOTIFY_LOCK();
cursor.visible = visible; cursor.visible = visible;
@ -763,19 +791,18 @@ screen_set_bg(Color color)
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_attr_enable(u8 attrs) screen_set_sgr(u8 attrs, bool ena)
{ {
if (ena) {
cursor.attrs |= attrs; cursor.attrs |= attrs;
} }
else {
void ICACHE_FLASH_ATTR
screen_attr_disable(u8 attrs)
{
cursor.attrs &= ~attrs; cursor.attrs &= ~attrs;
} }
}
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_inverse_enable(bool ena) screen_set_sgr_inverse(bool ena)
{ {
cursor.inverse = ena; cursor.inverse = ena;
} }
@ -829,12 +856,26 @@ screen_set_cursors_alt_mode(bool alt_mode)
NOTIFY_DONE(); NOTIFY_DONE();
} }
void ICACHE_FLASH_ATTR
screen_set_reverse_video(bool reverse)
{
NOTIFY_LOCK();
scr.reverse = reverse;
NOTIFY_DONE();
}
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
screen_set_newline_mode(bool nlm) screen_set_newline_mode(bool nlm)
{ {
scr.newline_mode = 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 void ICACHE_FLASH_ATTR
screen_report_sgr(char *buffer) screen_report_sgr(char *buffer)
{ {
@ -868,32 +909,31 @@ screen_putchar(const char *ch)
// Special treatment for CRLF // Special treatment for CRLF
switch (ch[0]) { switch (ch[0]) {
case '\r': case CR:
screen_cursor_set_x(0); screen_cursor_set_x(0);
if (scr.newline_mode) {
// like LF
screen_cursor_move(1, 0, true);
}
goto done; goto done;
case '\n': case LF:
screen_cursor_move(1, 0, true); // can scroll screen_cursor_move(1, 0, true); // can scroll
if (scr.newline_mode) {
// like CR
screen_cursor_set_x(0);
}
goto done; goto done;
case 8: // BS case BS:
if (cursor.x > 0) { 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) { if (cursor.hanging) {
cursor.hanging = false; cursor.hanging = false;
} }
cursor.x--; cursor.x--;
} }
// we should not wrap around // we should not wrap around, and backspace should not even clear the cell (verified in xterm)
// and apparently backspace should not even clear the cell
goto done; goto done;
default: default:
if (ch[0] < ' ') { if (ch[0] < SP) {
// Discard // Discard
warn("Ignoring control char %d", (int)ch[0]); warn("Ignoring control char %d", (int)ch[0]);
goto done; goto done;
@ -902,7 +942,7 @@ screen_putchar(const char *ch)
if (cursor.hanging) { if (cursor.hanging) {
// perform the scheduled wrap if 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) { if (cursor.wraparound) {
cursor.x = 0; cursor.x = 0;
cursor.y++; cursor.y++;
@ -1137,13 +1177,11 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
encode2B((u16) cursor.y, &w3); encode2B((u16) cursor.y, &w3);
encode2B((u16) cursor.x, &w4); encode2B((u16) cursor.x, &w4);
encode2B((u16) ( encode2B((u16) (
cursor.fg | (cursor.visible ? 0x01 : 0) |
(cursor.bg<<4) | (cursor.hanging ? 0x02 : 0) |
(cursor.visible ? 0x100 : 0) | (scr.cursors_alt_mode ? 0x04 : 0) |
(cursor.hanging ? 0x200 : 0) | (scr.numpad_alt_mode ? 0x08 : 0) |
(scr.cursors_alt_mode ? 0x400 : 0) | (termconf->fn_alt_mode ? 0x10 : 0)
(scr.numpad_alt_mode ? 0x800 : 0) |
(termconf->fn_alt_mode ? 0x1000 : 0)
) )
, &w5); , &w5);
@ -1171,8 +1209,20 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
// No repeat // No repeat
bool changeAttrs = cell0->attrs != ss->lastAttrs; bool changeAttrs = cell0->attrs != ss->lastAttrs;
bool changeColors = (cell0->fg != ss->lastFg || cell0->bg != ss->lastBg); 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) { if (!changeAttrs && changeColors) {
encode2B(cell0->fg | (cell0->bg<<4), &w1); encode2B(fg | (bg<<4), &w1);
bufprint("\x03%c%c", w1.lsb, w1.msb); bufprint("\x03%c%c", w1.lsb, w1.msb);
} }
else if (changeAttrs && !changeColors) { else if (changeAttrs && !changeColors) {
@ -1182,11 +1232,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
} }
else if (changeAttrs && changeColors) { else if (changeAttrs && changeColors) {
// colors and attrs // colors and attrs
encode3B((u32) ( encode3B((u32) (fg | (bg<<4) | (cell0->attrs<<8)), &lw1);
cell0->fg |
(cell0->bg<<4) |
(cell0->attrs<<8))
, &lw1);
bufprint("\x01%c%c%c", lw1.lsb, lw1.msb, lw1.xsb); bufprint("\x01%c%c%c", lw1.lsb, lw1.msb, lw1.xsb);
} }

@ -41,19 +41,6 @@
#define TERM_BTN_LEN 10 #define TERM_BTN_LEN 10
#define TERM_TITLE_LEN 64 #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_DISPLAY_TOUT_MS 20
#define SCR_DEF_PARSER_TOUT_MS 10 #define SCR_DEF_PARSER_TOUT_MS 10
#define SCR_DEF_FN_ALT_MODE false #define SCR_DEF_FN_ALT_MODE false
@ -64,10 +51,12 @@ typedef enum {
/** Maximum screen size (determines size of the static data array) */ /** Maximum screen size (determines size of the static data array) */
#define MAX_SCREEN_SIZE (80*25) #define MAX_SCREEN_SIZE (80*25)
// --- Persistent Settings ---
typedef struct { typedef struct {
u32 width; u32 width;
u32 height; u32 height;
u8 default_bg; u8 default_bg; // should be the Color typedef, but this way the size is more explicit
u8 default_fg; u8 default_fg;
char title[TERM_TITLE_LEN]; char title[TERM_TITLE_LEN];
char btn[5][TERM_BTN_LEN]; char btn[5][TERM_BTN_LEN];
@ -86,21 +75,18 @@ extern TerminalConfigBundle * const termconf;
*/ */
extern TerminalConfigBundle termconf_scratch; extern TerminalConfigBundle termconf_scratch;
/** Restore default settings to termconf. Does not apply or copy to scratch. */
void terminal_restore_defaults(void); void terminal_restore_defaults(void);
/** Apply settings, redraw (clears the screen) */
void terminal_apply_settings(void); void terminal_apply_settings(void);
void terminal_apply_settings_noclear(void); // the same, but with no screen reset / init /** Apply settings, redraw (no clear - not resized) */
void terminal_apply_settings_noclear(void);
void screen_report_sgr(char *buffer); /** Init the screen */
void screen_init(void);
typedef enum { /** Change the screen size */
CLEAR_TO_CURSOR=0, CLEAR_FROM_CURSOR=1, CLEAR_ALL=2 void screen_resize(int rows, int cols);
} ClearMode;
typedef uint8_t Color;
httpd_cgi_state screenSerializeToBuffer(char *buffer, size_t buf_len, void **data);
void screenSerializeLabelsToBuffer(char *buffer, size_t buf_len); // --- Encoding ---
typedef struct { typedef struct {
u8 lsb; u8 lsb;
@ -116,15 +102,18 @@ typedef struct {
/** Encode number to two nice ASCII bytes */ /** Encode number to two nice ASCII bytes */
void encode2B(u16 number, WordB2 *stru); void encode2B(u16 number, WordB2 *stru);
/** Init the screen */ httpd_cgi_state screenSerializeToBuffer(char *buffer, size_t buf_len, void **data);
void screen_init(void);
/** Change the screen size */ void screenSerializeLabelsToBuffer(char *buffer, size_t buf_len);
void screen_resize(int rows, int cols);
/** Check if coord is valid */
bool screen_isCoordValid(int y, int x);
// --- Clearing --- // --- Clearing ---
typedef enum {
CLEAR_TO_CURSOR = 0,
CLEAR_FROM_CURSOR = 1,
CLEAR_ALL = 2
} ClearMode;
/** Screen reset to default state */ /** Screen reset to default state */
void screen_reset(void); void screen_reset(void);
/** Clear entire screen */ /** Clear entire screen */
@ -133,18 +122,23 @@ void screen_clear(ClearMode mode);
void screen_clear_line(ClearMode mode); void screen_clear_line(ClearMode mode);
/** Clear part of line */ /** Clear part of line */
void screen_clear_in_line(unsigned int count); void screen_clear_in_line(unsigned int count);
/** Shift screen upwards */ /** Swap to alternate buffer (really what this does is backup terminal title, size and other global attributes) */
void screen_scroll_up(unsigned int lines); void screen_swap_state(bool alternate);
/** 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);
// --- insert / delete --- // --- insert / delete ---
/** Insert lines at cursor, shove down */
void screen_insert_lines(unsigned int lines); void screen_insert_lines(unsigned int lines);
/** Delete lines at cursor, pull up */
void screen_delete_lines(unsigned int lines); void screen_delete_lines(unsigned int lines);
/** Insert characters at cursor, shove right */
void screen_insert_characters(unsigned int count); void screen_insert_characters(unsigned int count);
/** Delete characters at cursor, pull left */
void screen_delete_characters(unsigned int count); 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 --- // --- Cursor control ---
@ -156,51 +150,81 @@ void screen_cursor_get(int *y, int *x);
void screen_cursor_set_x(int x); void screen_cursor_set_x(int x);
/** Set cursor Y position */ /** Set cursor Y position */
void screen_cursor_set_y(int y); void screen_cursor_set_y(int y);
/** Reset cursor attribs */
void screen_reset_sgr(void);
/** Relative cursor move */ /** Relative cursor move */
void screen_cursor_move(int dy, int dx, bool scroll); void screen_cursor_move(int dy, int dx, bool scroll);
/** Save the cursor pos */ /** Save the cursor pos */
void screen_cursor_save(bool withAttrs); void screen_cursor_save(bool withAttrs);
/** Restore the cursor pos */ /** Restore the cursor pos */
void screen_cursor_restore(bool withAttrs); void screen_cursor_restore(bool withAttrs);
/** Enable cursor display */
void screen_cursor_visible(bool visible); // --- Cursor behavior setting ---
/** Enable auto wrap */
void screen_wrap_enable(bool enable); /** Toggle INSERT / REPLACE */
void screen_set_insert_mode(bool insert);
/** Enable CR auto */ /** Enable CR auto */
void screen_set_newline_mode(bool nlm); 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 */ /** Set cursor foreground color */
void screen_set_fg(Color color); void screen_set_fg(Color color);
/** Set cursor background coloor */ /** Set cursor background coloor */
void screen_set_bg(Color color); 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 */ // --- Global modes and attributes ---
void screen_attr_enable(u8 attrs);
/** disable attrs by bitmask */ /** Enable cursor display */
void screen_attr_disable(u8 attrs); void screen_set_cursor_visible(bool visible);
/** Set the inverse cursor attribute */
void screen_inverse_enable(bool ena);
/** Toggle INSERT / REPLACE */
void screen_set_insert_mode(bool insert);
/** Toggle application keypad mode */ /** Toggle application keypad mode */
void screen_set_numpad_alt_mode(bool app_mode); void screen_set_numpad_alt_mode(bool app_mode);
/** Toggle application cursor mode */ /** Toggle application cursor mode */
void screen_set_cursors_alt_mode(bool app_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); void screen_set_charset_n(int Gx);
/** Assign G0 or G1 */
void screen_set_charset(int Gx, char charset); void screen_set_charset(int Gx, char charset);
// tabs // --- Tab stops ---
/** Remove all tabs on the screen */
void screen_clear_all_tabs(void); void screen_clear_all_tabs(void);
/** Set tab at the current column */
void screen_set_tab(void); void screen_set_tab(void);
/** Remove tab at the current column (if any) */
void screen_clear_tab(void); void screen_clear_tab(void);
void screen_tab_forward(void); /** Move forward one tab */
void screen_tab_reverse(void); 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. * 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) * unicode (then it can be 4 chars, or terminated by a zero)
*/ */
void screen_putchar(const char *ch); 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 // --- Notify ---
/** Debug dump */
void screen_dd(void);
#endif
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); extern void screen_notifyChange(ScreenNotifyChangeTopic topic);
#endif // SCREEN_H #endif // SCREEN_H

@ -1,7 +1,12 @@
#ifndef USER_MAIN_H_H #ifndef USER_MAIN_H_H
#define 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" #define TERMINAL_GITHUB_REPO "https://github.com/MightyPork/esp-vt100-firmware"
#endif //USER_MAIN_H_H #endif //USER_MAIN_H_H

Loading…
Cancel
Save