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