|
|
|
@ -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(); |
|
|
|
|
} |
|
|
|
|
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(); |
|
|
|
|
} |
|
|
|
|
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(); |
|
|
|
|
} |
|
|
|
|
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(); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'u': |
|
|
|
|
if (leadchar == NUL && count == 0) { |
|
|
|
|
screen_cursor_restore(0); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
warn_bad_csi(); |
|
|
|
|
} |
|
|
|
|
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,31 +358,13 @@ 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 (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); |
|
|
|
|
sprintf(resp_buf, "\033[%d;%dR", y + 1, x + 1); |
|
|
|
|
apars_respond(resp_buf); |
|
|
|
|
} |
|
|
|
|
else if (n1 == 5) { |
|
|
|
|
// Query device status - reply "Device is OK"
|
|
|
|
@ -376,28 +373,115 @@ apars_handle_csi(char leadchar, const int *params, int count, char keychar) |
|
|
|
|
else { |
|
|
|
|
warn_bad_csi(); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'c': // CSI-c - report capabilities
|
|
|
|
|
// 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); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
warn_bad_csi(); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* CSI none Pm ! key |
|
|
|
|
*/ |
|
|
|
|
static inline void ICACHE_FLASH_ATTR |
|
|
|
|
switch_csi_NoLeadInterBang(CSI_Data *opts) |
|
|
|
|
{ |
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
warn_bad_csi(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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; |
|
|
|
|
|
|
|
|
|
case 'c': // CSI-c - report capabilities
|
|
|
|
|
if (leadchar == NUL) { |
|
|
|
|
apars_respond("\033[?64;9c"); // pretend we're vt400 with national character sets
|
|
|
|
|
default: |
|
|
|
|
warn_bad_csi(); |
|
|
|
|
} |
|
|
|
|
else if (leadchar == '>') { |
|
|
|
|
// 41 - we're "VT400", 0 - ROM cartridge number
|
|
|
|
|
sprintf(buf, "\033[>41;%d;0c", FIRMWARE_VERSION_NUM); |
|
|
|
|
apars_respond(buf); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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 'x': // DECREQTPARM -> DECREPTPARM
|
|
|
|
|
do_csi_decreqtparm(&opts); |
|
|
|
|
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: |
|
|
|
@ -405,15 +489,95 @@ apars_handle_csi(char leadchar, const int *params, int count, char keychar) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* CSI [?] Pm {h|l} |
|
|
|
|
* CSI Pm m |
|
|
|
|
* @param opts |
|
|
|
|
*/ |
|
|
|
|
static void ICACHE_FLASH_ATTR do_csi_privattr(CSI_Data *opts) |
|
|
|
|
static inline void ICACHE_FLASH_ATTR |
|
|
|
|
do_csi_sgr(CSI_Data *opts) |
|
|
|
|
{ |
|
|
|
|
int count = opts->count; |
|
|
|
|
|
|
|
|
|
if (count == 0) { |
|
|
|
|
count = 1; // this makes it work as 0 (reset)
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// iterate arguments
|
|
|
|
|
for (int i = 0; i < count; i++) { |
|
|
|
|
int n = opts->n[i]; |
|
|
|
|
|
|
|
|
|
if (n == SGR_RESET) screen_reset_sgr(); |
|
|
|
|
// -- set color --
|
|
|
|
|
else if (n >= SGR_FG_START && n <= SGR_FG_END) screen_set_fg((Color) (n - SGR_FG_START)); // ANSI normal fg
|
|
|
|
|
else if (n >= SGR_BG_START && n <= SGR_BG_END) screen_set_bg((Color) (n - SGR_BG_START)); // ANSI normal bg
|
|
|
|
|
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_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_set_sgr(ATTR_BOLD, 0); // can also mean "Double Underline"
|
|
|
|
|
else if (n == SGR_OFF(SGR_FAINT)) screen_set_sgr(ATTR_FAINT | ATTR_BOLD, 0); // "normal"
|
|
|
|
|
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
|
|
|
|
|
else { |
|
|
|
|
ansi_noimpl("SGR %d", n); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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'); |
|
|
|
|
|
|
|
|
|
if (opts->lead == '?') { |
|
|
|
|
// --- DEC private attributes ---
|
|
|
|
|
for (int i = 0; i < opts->count; i++) { |
|
|
|
|
int n = opts->n[i]; |
|
|
|
@ -470,7 +634,11 @@ static void ICACHE_FLASH_ATTR do_csi_privattr(CSI_Data *opts) |
|
|
|
|
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); |
|
|
|
|
|
|
|
|
|
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
|
|
|
|
@ -516,19 +684,9 @@ static void ICACHE_FLASH_ATTR do_csi_privattr(CSI_Data *opts) |
|
|
|
|
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
|
|
|
|
|
ansi_noimpl("Bracketed paste"); |
|
|
|
|
} |
|
|
|
|
else if (n == 25) { |
|
|
|
|
screen_set_cursor_visible(yn); |
|
|
|
@ -542,89 +700,57 @@ static void ICACHE_FLASH_ATTR do_csi_privattr(CSI_Data *opts) |
|
|
|
|
screen_notifyChange(CHANGE_CONTENT); // this info is included in the screen preamble
|
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
ansi_noimpl("CSI ? %d %c", n, opts->key); |
|
|
|
|
ansi_noimpl("?OPTION %d", n); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
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 m |
|
|
|
|
* CSI Ps ; Ps ; Ps t |
|
|
|
|
* @param opts |
|
|
|
|
*/ |
|
|
|
|
static void ICACHE_FLASH_ATTR do_csi_sgr(CSI_Data *opts) |
|
|
|
|
static inline void ICACHE_FLASH_ATTR |
|
|
|
|
do_csi_xterm_screen_cmd(CSI_Data *opts) |
|
|
|
|
{ |
|
|
|
|
int count = opts->count; |
|
|
|
|
char resp_buf[20]; |
|
|
|
|
switch (opts->n[0]) { |
|
|
|
|
case 8: // set size
|
|
|
|
|
screen_resize(opts->n[1], opts->n[2]); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
if (count == 0) { |
|
|
|
|
count = 1; // this makes it work as 0 (reset)
|
|
|
|
|
} |
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
if (opts->lead != NUL) { |
|
|
|
|
// some xterm garbage - discard
|
|
|
|
|
// CSI > Ps; Ps m
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
case 20: // Report icon
|
|
|
|
|
case 21: // Report title
|
|
|
|
|
apars_respond("\033]l"); |
|
|
|
|
apars_respond(termconf_scratch.title); |
|
|
|
|
apars_respond("\033\\"); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
// iterate arguments
|
|
|
|
|
for (int i = 0; i < count; i++) { |
|
|
|
|
int n = opts->n[i]; |
|
|
|
|
case 22: |
|
|
|
|
ansi_noimpl("Push title"); |
|
|
|
|
break; |
|
|
|
|
case 23: |
|
|
|
|
ansi_noimpl("Pop title"); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
if (n == SGR_RESET) screen_reset_sgr(); |
|
|
|
|
// -- set color --
|
|
|
|
|
else if (n >= SGR_FG_START && n <= SGR_FG_END) screen_set_fg((Color) (n - SGR_FG_START)); // ANSI normal fg
|
|
|
|
|
else if (n >= SGR_BG_START && n <= SGR_BG_END) screen_set_bg((Color) (n - SGR_BG_START)); // ANSI normal bg
|
|
|
|
|
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_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_set_sgr(ATTR_BOLD, 0); // can also mean "Double Underline"
|
|
|
|
|
else if (n == SGR_OFF(SGR_FAINT)) screen_set_sgr(ATTR_FAINT | ATTR_BOLD, 0); // "normal"
|
|
|
|
|
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
|
|
|
|
|
else { |
|
|
|
|
ansi_noimpl("SGR %d", n); |
|
|
|
|
} |
|
|
|
|
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]; |
|
|
|
|
|
|
|
|
|