Tab Stops™

pull/111/merge
Ondřej Hruška 7 years ago
parent 184b186b85
commit 68747d493c
  1. 51
      user/ansi_parser.c
  2. 5
      user/ansi_parser.rl
  3. 43
      user/ansi_parser_callbacks.c
  4. 140
      user/screen.c
  5. 8
      user/screen.h

@ -150,11 +150,14 @@ ansi_parser(char newchar)
newchar = LF; // translate to LF, like VT100 / xterm do newchar = LF; // translate to LF, like VT100 / xterm do
case CR: case CR:
case LF: case LF:
case TAB:
case BS: case BS:
apars_handle_plainchar(newchar); apars_handle_plainchar(newchar);
return; return;
case TAB:
screen_tab_forward();
return;
// Select G0 or G1 // Select G0 or G1
case SI: case SI:
screen_set_charset_n(1); screen_set_charset_n(1);
@ -202,7 +205,7 @@ ansi_parser(char newchar)
// The parser // The parser
/* #line 206 "user/ansi_parser.c" */ /* #line 209 "user/ansi_parser.c" */
{ {
const char *_acts; const char *_acts;
unsigned int _nacts; unsigned int _nacts;
@ -488,7 +491,7 @@ execFuncs:
while ( _nacts-- > 0 ) { while ( _nacts-- > 0 ) {
switch ( *_acts++ ) { switch ( *_acts++ ) {
case 0: case 0:
/* #line 177 "user/ansi_parser.rl" */ /* #line 180 "user/ansi_parser.rl" */
{ {
if ((*p) != 0) { if ((*p) != 0) {
apars_handle_plainchar((*p)); apars_handle_plainchar((*p));
@ -496,7 +499,7 @@ execFuncs:
} }
break; break;
case 1: case 1:
/* #line 186 "user/ansi_parser.rl" */ /* #line 189 "user/ansi_parser.rl" */
{ {
// Reset the CSI builder // Reset the CSI builder
csi_leading = csi_char = 0; csi_leading = csi_char = 0;
@ -512,13 +515,13 @@ execFuncs:
} }
break; break;
case 2: case 2:
/* #line 200 "user/ansi_parser.rl" */ /* #line 203 "user/ansi_parser.rl" */
{ {
csi_leading = (*p); csi_leading = (*p);
} }
break; break;
case 3: case 3:
/* #line 204 "user/ansi_parser.rl" */ /* #line 207 "user/ansi_parser.rl" */
{ {
if (csi_cnt == 0) csi_cnt = 1; if (csi_cnt == 0) csi_cnt = 1;
// x10 + digit // x10 + digit
@ -528,7 +531,7 @@ execFuncs:
} }
break; break;
case 4: case 4:
/* #line 212 "user/ansi_parser.rl" */ /* #line 215 "user/ansi_parser.rl" */
{ {
if (csi_cnt == 0) csi_cnt = 1; // handle case when first arg is empty if (csi_cnt == 0) csi_cnt = 1; // handle case when first arg is empty
csi_cnt++; csi_cnt++;
@ -536,7 +539,7 @@ execFuncs:
} }
break; break;
case 5: case 5:
/* #line 218 "user/ansi_parser.rl" */ /* #line 221 "user/ansi_parser.rl" */
{ {
csi_char = (*p); csi_char = (*p);
apars_handle_CSI(csi_leading, csi_n, csi_cnt, csi_char); apars_handle_CSI(csi_leading, csi_n, csi_cnt, csi_char);
@ -544,7 +547,7 @@ execFuncs:
} }
break; break;
case 6: case 6:
/* #line 224 "user/ansi_parser.rl" */ /* #line 227 "user/ansi_parser.rl" */
{ {
ansi_warn("Parser error."); ansi_warn("Parser error.");
apars_handle_badseq(); apars_handle_badseq();
@ -552,7 +555,7 @@ execFuncs:
} }
break; break;
case 7: case 7:
/* #line 242 "user/ansi_parser.rl" */ /* #line 245 "user/ansi_parser.rl" */
{ {
csi_ni = 0; csi_ni = 0;
@ -570,7 +573,7 @@ execFuncs:
} }
break; break;
case 8: case 8:
/* #line 259 "user/ansi_parser.rl" */ /* #line 262 "user/ansi_parser.rl" */
{ {
osc_bi = 0; osc_bi = 0;
osc_buffer[0] = '\0'; osc_buffer[0] = '\0';
@ -579,7 +582,7 @@ execFuncs:
} }
break; break;
case 9: case 9:
/* #line 266 "user/ansi_parser.rl" */ /* #line 269 "user/ansi_parser.rl" */
{ {
apars_handle_OSC_SetScreenSize(csi_n[0], csi_n[1]); apars_handle_OSC_SetScreenSize(csi_n[0], csi_n[1]);
inside_osc = false; inside_osc = false;
@ -587,13 +590,13 @@ execFuncs:
} }
break; break;
case 10: case 10:
/* #line 272 "user/ansi_parser.rl" */ /* #line 275 "user/ansi_parser.rl" */
{ {
osc_buffer[osc_bi++] = (*p); osc_buffer[osc_bi++] = (*p);
} }
break; break;
case 11: case 11:
/* #line 276 "user/ansi_parser.rl" */ /* #line 279 "user/ansi_parser.rl" */
{ {
osc_buffer[osc_bi++] = '\0'; osc_buffer[osc_bi++] = '\0';
apars_handle_OSC_SetTitle(osc_buffer); apars_handle_OSC_SetTitle(osc_buffer);
@ -602,7 +605,7 @@ execFuncs:
} }
break; break;
case 12: case 12:
/* #line 283 "user/ansi_parser.rl" */ /* #line 286 "user/ansi_parser.rl" */
{ {
osc_buffer[osc_bi++] = '\0'; osc_buffer[osc_bi++] = '\0';
apars_handle_OSC_SetButton(csi_n[0], osc_buffer); apars_handle_OSC_SetButton(csi_n[0], osc_buffer);
@ -611,28 +614,28 @@ execFuncs:
} }
break; break;
case 13: case 13:
/* #line 316 "user/ansi_parser.rl" */ /* #line 319 "user/ansi_parser.rl" */
{ {
apars_handle_hashCode((*p)); apars_handle_hashCode((*p));
{cs = 1;goto _again;} {cs = 1;goto _again;}
} }
break; break;
case 14: case 14:
/* #line 321 "user/ansi_parser.rl" */ /* #line 324 "user/ansi_parser.rl" */
{ {
apars_handle_shortCode((*p)); apars_handle_shortCode((*p));
{cs = 1;goto _again;} {cs = 1;goto _again;}
} }
break; break;
case 15: case 15:
/* #line 326 "user/ansi_parser.rl" */ /* #line 329 "user/ansi_parser.rl" */
{ {
apars_handle_setXCtrls((*p)); // weird control settings like 7 bit / 8 bit mode apars_handle_setXCtrls((*p)); // weird control settings like 7 bit / 8 bit mode
{cs = 1;goto _again;} {cs = 1;goto _again;}
} }
break; break;
case 16: case 16:
/* #line 331 "user/ansi_parser.rl" */ /* #line 334 "user/ansi_parser.rl" */
{ {
// abuse the buffer for storing the leading char // abuse the buffer for storing the leading char
osc_buffer[0] = (*p); osc_buffer[0] = (*p);
@ -640,13 +643,13 @@ execFuncs:
} }
break; break;
case 17: case 17:
/* #line 337 "user/ansi_parser.rl" */ /* #line 340 "user/ansi_parser.rl" */
{ {
apars_handle_characterSet(osc_buffer[0], (*p)); apars_handle_characterSet(osc_buffer[0], (*p));
{cs = 1;goto _again;} {cs = 1;goto _again;}
} }
break; break;
/* #line 650 "user/ansi_parser.c" */ /* #line 653 "user/ansi_parser.c" */
} }
} }
goto _again; goto _again;
@ -664,7 +667,7 @@ _again:
while ( __nacts-- > 0 ) { while ( __nacts-- > 0 ) {
switch ( *__acts++ ) { switch ( *__acts++ ) {
case 6: case 6:
/* #line 224 "user/ansi_parser.rl" */ /* #line 227 "user/ansi_parser.rl" */
{ {
ansi_warn("Parser error."); ansi_warn("Parser error.");
apars_handle_badseq(); apars_handle_badseq();
@ -673,7 +676,7 @@ _again:
goto _again;} goto _again;}
} }
break; break;
/* #line 677 "user/ansi_parser.c" */ /* #line 680 "user/ansi_parser.c" */
} }
} }
} }
@ -681,7 +684,7 @@ goto _again;}
_out: {} _out: {}
} }
/* #line 361 "user/ansi_parser.rl" */ /* #line 364 "user/ansi_parser.rl" */
} }

@ -114,11 +114,14 @@ ansi_parser(char newchar)
newchar = LF; // translate to LF, like VT100 / xterm do newchar = LF; // translate to LF, like VT100 / xterm do
case CR: case CR:
case LF: case LF:
case TAB:
case BS: case BS:
apars_handle_plainchar(newchar); apars_handle_plainchar(newchar);
return; return;
case TAB:
screen_tab_forward();
return;
// Select G0 or G1 // Select G0 or G1
case SI: case SI:
screen_set_charset_n(1); screen_set_charset_n(1);

@ -136,6 +136,8 @@ apars_handle_CSI(char leadchar, int *params, int count, char keychar)
case 'M': case 'M':
case '@': case '@':
case 'P': case 'P':
case 'I':
case 'Z':
if (n1 == 0) n1 = 1; if (n1 == 0) n1 = 1;
break; break;
@ -257,14 +259,26 @@ apars_handle_CSI(char leadchar, int *params, int count, char keychar)
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 == 25) { if (n == 1) {
screen_cursor_visible(yn); screen_set_cursors_alt_mode(yn);
}
else if (n == 6) {
// TODO origin mode (scrolling region)
} }
else if (n == 7) { else if (n == 7) {
screen_wrap_enable(yn); screen_wrap_enable(yn);
} }
else if (n == 1) { else if (n == 8) {
screen_set_cursors_alt_mode(yn); // TODO autorepeat mode
}
else if (n == 9) {
// TODO X10 mouse
}
else if (n == 1000) {
// TODo X11 mouse
}
else if (n == 25) {
screen_cursor_visible(yn);
} }
else { else {
// ansi_warn("NOIMPL DEC opt %d", n); // ansi_warn("NOIMPL DEC opt %d", n);
@ -371,13 +385,23 @@ apars_handle_CSI(char leadchar, int *params, int count, char keychar)
break; break;
case 'g': case 'g':
// TODO clear tab if (n1 == 3) {
// ansi_warn("NOIMPL clear tab"); screen_clear_all_tabs();
} else {
screen_clear_tab();
}
break; break;
case 'Z': case 'Z':
// TODO back tab for(; n1 > 0; n1--) {
// ansi_warn("NOIMPL cursor back tab"); screen_tab_reverse();
}
break;
case 'I':
for(; n1 > 0; n1--) {
screen_tab_forward();
}
break; break;
case 'c': // CSI-c case 'c': // CSI-c
@ -438,8 +462,7 @@ void ICACHE_FLASH_ATTR apars_handle_shortCode(char c)
break; break;
case 'H': case 'H':
// TODO set tab screen_set_tab();
// ansi_warn("NOIMPL set tab");
break; break;
case '>': case '>':

@ -32,6 +32,13 @@ typedef struct __attribute__((packed)){
*/ */
static Cell screen[MAX_SCREEN_SIZE]; static Cell screen[MAX_SCREEN_SIZE];
#define TABSTOP_WORDS 5
/**
* Tab stops bitmap
*/
static u32 tab_stops[TABSTOP_WORDS];
/** /**
* Screen state structure * Screen state structure
*/ */
@ -204,8 +211,124 @@ screen_reset(void)
// size is left unchanged // size is left unchanged
screen_clear(CLEAR_ALL); screen_clear(CLEAR_ALL);
screen_clear_all_tabs();
NOTIFY_DONE();
}
//endregion
//region --- Tab stops ---
void ICACHE_FLASH_ATTR
screen_clear_all_tabs(void)
{
memset(tab_stops, 0, sizeof(tab_stops));
}
void ICACHE_FLASH_ATTR
screen_set_tab(void)
{
tab_stops[cursor.x/32] |= (1<<(cursor.x%32));
}
void ICACHE_FLASH_ATTR
screen_clear_tab(void)
{
tab_stops[cursor.x/32] &= ~(1<<(cursor.x%32));
}
/**
* Find tab stop closest to cursor to the right
* @return X pos or -1
*/
static int ICACHE_FLASH_ATTR
next_tab_stop(void)
{
// cursor must never go past EOL
if (cursor.x >= W-1) return -1;
// find first word to inspect
int idx = (cursor.x+1)/32;
int offs = (cursor.x+1)%32;
int cp = cursor.x;
while (idx < TABSTOP_WORDS) {
u32 w = tab_stops[idx];
w >>= offs;
for(;offs<32;offs++) {
cp++;
if (cp >= W) return -1;
if (w & 1) return cp;
w >>= 1;
}
offs = 0;
idx++;
}
return -1;
}
/**
* Find tab stop closest to cursor to the left
* @return X pos or -1
*/
static int ICACHE_FLASH_ATTR
prev_tab_stop(void)
{
// nowhere to go
if (cursor.x == 0) return -1;
// find first word to inspect
int idx = (cursor.x-1)/32;
int offs = (cursor.x-1)%32;
int cp = cursor.x;
while (idx >= 0) {
u32 w = tab_stops[idx];
w <<= 31-offs;
if (w == 0) {
cp -= cp%32;
if (cp < 0) return -1;
goto next;
}
for(;offs>=0;offs--) {
cp--;
if (cp < 0) return -1;
if (w & (1<<31)) return cp;
w <<= 1;
}
next:
offs = 31;
idx--;
}
return -1;
}
void ICACHE_FLASH_ATTR
screen_tab_forward(void)
{
NOTIFY_LOCK();
int tab = next_tab_stop();
if (tab != -1) {
cursor.x = tab;
} else {
cursor.x = W-1;
}
NOTIFY_DONE(); NOTIFY_DONE();
} }
void ICACHE_FLASH_ATTR
screen_tab_reverse(void)
{
NOTIFY_LOCK();
int tab = prev_tab_stop();
if (tab != -1) {
cursor.x = tab;
} else {
cursor.x = 0;
}
NOTIFY_DONE();
}
//endregion //endregion
//region --- Clearing & inserting --- //region --- Clearing & inserting ---
@ -742,29 +865,16 @@ screen_putchar(const char *ch)
case 8: // BS case 8: // BS
if (cursor.x > 0) { if (cursor.x > 0) {
// according to vttest, backspace should go to col 79 if "hanging" after 80
if (cursor.hanging) { if (cursor.hanging) {
cursor.hanging = false; cursor.hanging = false;
} else {
cursor.x--;
} }
cursor.x--;
} }
// we should not wrap around // we should not wrap around
// and apparently backspace should not even clear the cell // and apparently backspace should not even clear the cell
goto done; goto done;
case 9: // TAB
// // TODO change to "go to next tab stop"
// if (cursor.x<((W-1)-(W-1)%4)) {
// c->c[0] = ' ';
// c->c[1] = 0;
// c->c[2] = 0;
// c->c[3] = 0;
// do {
// screen_putchar(" ");
// } while(cursor.x%4!=0);
// }
goto done;
default: default:
if (ch[0] < ' ') { if (ch[0] < ' ') {
// Discard // Discard

@ -193,6 +193,14 @@ void screen_set_cursors_alt_mode(bool app_mode);
void screen_set_charset_n(int Gx); void screen_set_charset_n(int Gx);
void screen_set_charset(int Gx, char charset); void screen_set_charset(int Gx, char charset);
// tabs
void screen_clear_all_tabs(void);
void screen_set_tab(void);
void screen_clear_tab(void);
void screen_tab_forward(void);
void screen_tab_reverse(void);
/** /**
* Set a character in the cursor color, move to right with wrap. * Set a character in the cursor color, move to right with wrap.
* The character may be ASCII (then only one char is used), or * The character may be ASCII (then only one char is used), or

Loading…
Cancel
Save