wip: vt parsing: initial csi/osc dispatching

This commit is contained in:
Daniel Eklöf 2019-06-15 22:22:44 +02:00
parent 0e6aa61d69
commit 2a4c08b941
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
10 changed files with 571 additions and 103 deletions

87
csi.c Normal file
View file

@ -0,0 +1,87 @@
#include "csi.h"
#define LOG_MODULE "csi"
#define LOG_ENABLE_DBG 1
#include "log.h"
static bool
csi_sgr(struct terminal *term)
{
for (size_t i = 0; i < term->vt.params.idx; i++) {
switch (term->vt.params.v[i].value) {
case 0:
term->vt.bold = false;
term->vt.dim = false;
term->vt.italic = false;
term->vt.underline = false;
term->vt.strikethrough = false;
term->vt.blink = false;
term->vt.conceal = false;
term->vt.reverse = false;
term->vt.foreground = term->grid.foreground;
term->vt.background = term->grid.background;
break;
case 1: term->vt.bold = true; break;
case 2: term->vt.dim = true; break;
case 3: term->vt.italic = true; break;
case 4: term->vt.underline = true; break;
case 5: term->vt.blink = true; break;
case 6: term->vt.blink = true; break;
case 7: term->vt.reverse = true; break;
case 8: term->vt.conceal = true; break;
case 9: term->vt.strikethrough = true; break;
case 22: term->vt.bold = term->vt.dim = false; break;
case 23: term->vt.italic = false; break;
case 24: term->vt.underline = false; break;
case 25: term->vt.blink = false; break;
case 27: term->vt.reverse = false; break;
case 28: term->vt.conceal = false; break;
case 29: term->vt.strikethrough = false; break;
case 30: term->vt.foreground = 0x000000ff; break;
case 31: term->vt.foreground = 0xff0000ff; break;
case 32: term->vt.foreground = 0x00ff00ff; break;
case 33: term->vt.foreground = 0xf0f000ff; break;
case 34: term->vt.foreground = 0x0000ffff; break;
case 35: term->vt.foreground = 0xf000f0ff; break;
case 36: term->vt.foreground = 0x00f0f0ff; break;
case 37: term->vt.foreground = 0xffffffff; break;
case 39: term->vt.foreground = term->grid.foreground; break;
case 40: term->vt.background = 0x000000ff; break;
case 41: term->vt.background = 0xff0000ff; break;
case 42: term->vt.background = 0x00ff00ff; break;
case 43: term->vt.background = 0xf0f000ff; break;
case 44: term->vt.background = 0x0000ffff; break;
case 45: term->vt.background = 0xf000f0ff; break;
case 46: term->vt.background = 0x00f0f0ff; break;
case 47: term->vt.background = 0xffffffff; break;
case 49: term->vt.background = term->grid.background; break;
default:
LOG_ERR("unimplemented: CSI: SGR: %u", term->vt.params.v[i].value);
return false;
}
}
return true;
}
bool
csi_dispatch(struct terminal *term, uint8_t final)
{
LOG_DBG("CSI: %zu paramaters, final = %c", term->vt.params.idx, final);
for (size_t i = 0; i < term->vt.params.idx; i++) {
LOG_DBG(" #%zu: %u", i, term->vt.params.v[i].value);
for (size_t j = 0; j < term->vt.params.v[i].sub.idx; j++)
LOG_DBG(" #%zu: %u", j, term->vt.params.v[i].sub.value[j]);
}
if (final == 'm' && term->vt.intermediates.idx == 0) {
return csi_sgr(term);
}
return true;
}

6
csi.h Normal file
View file

@ -0,0 +1,6 @@
#pragma once
#include <stdbool.h>
#include "terminal.h"
bool csi_dispatch(struct terminal *term, uint8_t final);

162
main.c
View file

@ -8,6 +8,9 @@
#include <locale.h> #include <locale.h>
#include <poll.h> #include <poll.h>
#include <sys/ioctl.h>
//#include <termios.h>
#include <wayland-client.h> #include <wayland-client.h>
#include <xdg-shell.h> #include <xdg-shell.h>
@ -18,6 +21,8 @@
#include "font.h" #include "font.h"
#include "shm.h" #include "shm.h"
#include "slave.h" #include "slave.h"
#include "terminal.h"
#include "vt.h"
static const uint32_t default_foreground = 0xffffffff; static const uint32_t default_foreground = 0xffffffff;
static const uint32_t default_background = 0x000000ff; static const uint32_t default_background = 0x000000ff;
@ -33,29 +38,6 @@ struct wayland {
struct xdg_toplevel *xdg_toplevel; struct xdg_toplevel *xdg_toplevel;
}; };
struct cell {
char c[5];
uint32_t foreground;
uint32_t background;
bool dirty;
};
struct grid {
int cols;
int rows;
int cell_width;
int cell_height;
int cursor;
struct cell *cells;
uint32_t foreground;
uint32_t background;
bool dirty;
bool all_dirty;
};
struct context { struct context {
bool quit; bool quit;
int ptmx; int ptmx;
@ -67,7 +49,8 @@ struct context {
int height; int height;
struct wayland wl; struct wayland wl;
struct grid grid; //struct grid grid;
struct terminal term;
bool frame_is_scheduled; bool frame_is_scheduled;
}; };
@ -83,7 +66,7 @@ static const struct wl_callback_listener frame_listener = {
static void static void
grid_render(struct context *c) grid_render(struct context *c)
{ {
assert(c->grid.dirty); assert(c->term.grid.dirty);
assert(c->width > 0); assert(c->width > 0);
assert(c->height > 0); assert(c->height > 0);
@ -95,7 +78,7 @@ grid_render(struct context *c)
cairo_set_operator(buf->cairo, CAIRO_OPERATOR_SOURCE); cairo_set_operator(buf->cairo, CAIRO_OPERATOR_SOURCE);
cairo_set_scaled_font(buf->cairo, c->font); cairo_set_scaled_font(buf->cairo, c->font);
if (c->grid.all_dirty) { if (c->term.grid.all_dirty) {
br = (double)((default_background >> 24) & 0xff) / 255.0; br = (double)((default_background >> 24) & 0xff) / 255.0;
bg = (double)((default_background >> 16) & 0xff) / 255.0; bg = (double)((default_background >> 16) & 0xff) / 255.0;
bb = (double)((default_background >> 8) & 0xff) / 255.0; bb = (double)((default_background >> 8) & 0xff) / 255.0;
@ -105,20 +88,20 @@ grid_render(struct context *c)
} }
for (int row = 0; row < c->grid.rows; row++) { for (int row = 0; row < c->term.grid.rows; row++) {
for (int col = 0; col < c->grid.cols; col++) { for (int col = 0; col < c->term.grid.cols; col++) {
int cell_idx = row * c->grid.cols + col; int cell_idx = row * c->term.grid.cols + col;
struct cell *cell = &c->grid.cells[cell_idx]; struct cell *cell = &c->term.grid.cells[cell_idx];
if (!cell->dirty && !c->grid.all_dirty) if (!cell->dirty && !c->term.grid.all_dirty)
continue; continue;
cell->dirty = false; cell->dirty = false;
bool has_cursor = c->grid.cursor == cell_idx; bool has_cursor = c->term.grid.cursor == cell_idx;
int y_ofs = row * c->grid.cell_height + c->fextents.ascent; int y_ofs = row * c->term.grid.cell_height + c->fextents.ascent;
int x_ofs = col * c->grid.cell_width; int x_ofs = col * c->term.grid.cell_width;
int damage_x = x_ofs; int damage_x = x_ofs;
int damage_y = y_ofs - c->fextents.ascent; int damage_y = y_ofs - c->fextents.ascent;
@ -141,7 +124,7 @@ grid_render(struct context *c)
cairo_rectangle( cairo_rectangle(
buf->cairo, damage_x, damage_y, buf->cairo, damage_x, damage_y,
c->grid.cell_width, c->grid.cell_height); c->term.grid.cell_width, c->term.grid.cell_height);
cairo_fill(buf->cairo); cairo_fill(buf->cairo);
cairo_glyph_t *glyphs = NULL; cairo_glyph_t *glyphs = NULL;
@ -167,7 +150,7 @@ grid_render(struct context *c)
wl_surface_damage_buffer( wl_surface_damage_buffer(
c->wl.surface, damage_x, damage_y, c->wl.surface, damage_x, damage_y,
c->grid.cell_width, c->grid.cell_height); c->term.grid.cell_width, c->term.grid.cell_height);
} }
} }
@ -178,7 +161,7 @@ grid_render(struct context *c)
c->frame_is_scheduled = true; c->frame_is_scheduled = true;
wl_surface_commit(c->wl.surface); wl_surface_commit(c->wl.surface);
c->grid.dirty = c->grid.all_dirty = false; c->term.grid.dirty = c->term.grid.all_dirty = false;
} }
static void static void
@ -189,7 +172,7 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da
c->frame_is_scheduled = false; c->frame_is_scheduled = false;
wl_callback_destroy(wl_callback); wl_callback_destroy(wl_callback);
if (c->grid.dirty) if (c->term.grid.dirty)
grid_render(c); grid_render(c);
} }
@ -202,25 +185,40 @@ resize(struct context *c, int width, int height)
c->width = width; c->width = width;
c->height = height; c->height = height;
size_t old_cells_len = c->grid.cols * c->grid.rows; size_t old_cells_len = c->term.grid.cols * c->term.grid.rows;
c->grid.cell_width = (int)ceil(c->fextents.max_x_advance); c->term.grid.cell_width = (int)ceil(c->fextents.max_x_advance);
c->grid.cell_height = (int)ceil(c->fextents.height); c->term.grid.cell_height = (int)ceil(c->fextents.height);
c->grid.cols = c->width / c->grid.cell_width; c->term.grid.cols = c->width / c->term.grid.cell_width;
c->grid.rows = c->height / c->grid.cell_height; c->term.grid.rows = c->height / c->term.grid.cell_height;
c->grid.cells = realloc(c->grid.cells, c->term.grid.cells = realloc(c->term.grid.cells,
c->grid.cols * c->grid.rows * sizeof(c->grid.cells[0])); c->term.grid.cols * c->term.grid.rows * sizeof(c->term.grid.cells[0]));
size_t new_cells_len = c->grid.cols * c->grid.rows; size_t new_cells_len = c->term.grid.cols * c->term.grid.rows;
for (size_t i = old_cells_len; i < new_cells_len; i++) { for (size_t i = old_cells_len; i < new_cells_len; i++) {
c->grid.cells[i] = (struct cell){.foreground = default_foreground, c->term.grid.cells[i] = (struct cell){.foreground = default_foreground,
.background = default_background}; .background = default_background};
} }
LOG_DBG("resize: %dx%d, grid: cols=%d, rows=%d", LOG_DBG("resize: %dx%d, grid: cols=%d, rows=%d",
c->width, c->height, c->grid.cols, c->grid.rows); c->width, c->height, c->term.grid.cols, c->term.grid.rows);
c->grid.dirty = c->grid.all_dirty = true; /* Update environment variables */
char cols_s[12], rows_s[12];
sprintf(cols_s, "%u", c->term.grid.cols);
sprintf(rows_s, "%u", c->term.grid.rows);
setenv("COLUMNS", cols_s, 1);
setenv("LINES", rows_s, 1);
/* SIignal TIOCSWINSZ */
if (ioctl(c->ptmx, TIOCSWINSZ,
&(struct winsize){.ws_row = c->term.grid.rows,
.ws_col = c->term.grid.cols}) == -1)
{
LOG_ERRNO("TIOCSWINSZ");
}
c->term.grid.dirty = c->term.grid.all_dirty = true;
if (!c->frame_is_scheduled) if (!c->frame_is_scheduled)
grid_render(c); grid_render(c);
@ -378,8 +376,13 @@ main(int argc, const char *const *argv)
struct context c = { struct context c = {
.quit = false, .quit = false,
.ptmx = posix_openpt(O_RDWR | O_NOCTTY), .ptmx = posix_openpt(O_RDWR | O_NOCTTY),
.grid = {.foreground = default_foreground, .term = {
.background = default_background}, .vt = {
.state = 1,
},
.grid = {.foreground = default_foreground,
.background = default_background},
},
}; };
const char *font_name = "Dina:pixelsize=12"; const char *font_name = "Dina:pixelsize=12";
@ -447,7 +450,7 @@ main(int argc, const char *const *argv)
/* TODO: use font metrics to calculate initial size from ROWS x COLS */ /* TODO: use font metrics to calculate initial size from ROWS x COLS */
const int default_width = 300; const int default_width = 300;
const int default_height = 300; const int default_height = 300;
c.grid.dirty = c.grid.all_dirty = true; c.term.grid.dirty = c.term.grid.all_dirty = true;
resize(&c, default_width, default_height); resize(&c, default_width, default_height);
wl_display_dispatch_pending(c.wl.display); wl_display_dispatch_pending(c.wl.display);
@ -492,7 +495,7 @@ main(int argc, const char *const *argv)
} }
if (fds[1].revents & POLLIN) { if (fds[1].revents & POLLIN) {
char data[1024]; uint8_t data[1024];
ssize_t count = read(c.ptmx, data, sizeof(data)); ssize_t count = read(c.ptmx, data, sizeof(data));
if (count < 0) { if (count < 0) {
LOG_ERRNO("failed to read from pseudo terminal"); LOG_ERRNO("failed to read from pseudo terminal");
@ -501,55 +504,8 @@ main(int argc, const char *const *argv)
//LOG_DBG("%.*s", (int)count, data); //LOG_DBG("%.*s", (int)count, data);
int new_cursor = c.grid.cursor; vt_from_slave(&c.term, data, count);
for (int i = 0; i < count;) { if (c.term.grid.dirty && !c.frame_is_scheduled)
switch (data[i]) {
case '\r':
new_cursor = new_cursor / c.grid.cols * c.grid.cols;
i++;
break;
case '\n':
new_cursor += c.grid.cols;
i++;
break;
case '\t':
new_cursor = (new_cursor + 8) / 8 * 8;
i++;
break;
default: {
/* TODO: mbrlen() + error handling */
int clen = mblen(&data[i], count - i);
assert(clen >= 0);
assert(i + clen <= count);
struct cell *cell = &c.grid.cells[new_cursor];
cell->dirty = true;
cell->foreground = c.grid.foreground;
cell->background = c.grid.background;
memcpy(cell->c, &data[i], clen);
cell->c[clen] = '\0';
new_cursor++;
i += clen;
break;
}
}
}
c.grid.dirty = true;
if (new_cursor != c.grid.cursor) {
c.grid.cells[c.grid.cursor].dirty = true;
c.grid.cells[new_cursor].dirty = true;
c.grid.cursor = new_cursor;
}
if (!c.frame_is_scheduled)
grid_render(&c); grid_render(&c);
} }
@ -578,7 +534,7 @@ out:
if (c.wl.display != NULL) if (c.wl.display != NULL)
wl_display_disconnect(c.wl.display); wl_display_disconnect(c.wl.display);
free(c.grid.cells); free(c.term.grid.cells);
if (c.font != NULL) if (c.font != NULL)
cairo_scaled_font_destroy(c.font); cairo_scaled_font_destroy(c.font);

View file

@ -57,12 +57,16 @@ endforeach
executable( executable(
'f00ter', 'f00ter',
'csi.c', 'csi.h',
'font.c', 'font.h', 'font.c', 'font.h',
'log.c', 'log.h', 'log.c', 'log.h',
'main.c', 'main.c',
'osc.c', 'osc.h',
'shm.c', 'shm.h', 'shm.c', 'shm.h',
'slave.c', 'slave.h', 'slave.c', 'slave.h',
'terminal.h',
'tllist.h', 'tllist.h',
'vt.c', 'vt.h',
wl_proto_src + wl_proto_headers, wl_proto_src + wl_proto_headers,
dependencies: [math, cairo, cairo_ft, fontconfig, wayland_client, wayland_cursor, xkb], dependencies: [math, cairo, cairo_ft, fontconfig, wayland_client, wayland_cursor, xkb],
install: true) install: true)

12
osc.c Normal file
View file

@ -0,0 +1,12 @@
#include "osc.h"
#define LOG_MODULE "osc"
#define LOG_ENABLE_DBG 1
#include "log.h"
bool
osc_dispatch(struct terminal *term)
{
LOG_DBG("OCS: %.*s", (int)term->vt.osc.idx, term->vt.osc.data);
return true;
}

6
osc.h Normal file
View file

@ -0,0 +1,6 @@
#pragma once
#include <stdbool.h>
#include "terminal.h"
bool osc_dispatch(struct terminal *term);

View file

@ -52,6 +52,10 @@ slave_spawn(int ptmx)
close(pts); close(pts);
pts = -1; pts = -1;
execl("/usr/bin/zsh", "/usr/bin/zsh", NULL);
#if 0
/* TODO: exec() */ /* TODO: exec() */
sleep(1); sleep(1);
const char *s = "hello\tbla\nfoobar\thaa"; const char *s = "hello\tbla\nfoobar\thaa";
@ -64,6 +68,7 @@ slave_spawn(int ptmx)
write(STDOUT_FILENO, "\råäö", strlen("\råäö")); write(STDOUT_FILENO, "\råäö", strlen("\råäö"));
sleep(1000); sleep(1000);
#endif
err: err:
if (pts != -1) if (pts != -1)

70
terminal.h Normal file
View file

@ -0,0 +1,70 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
struct cell {
char c[5];
uint32_t foreground;
uint32_t background;
bool dirty;
};
struct grid {
int cols;
int rows;
int cell_width;
int cell_height;
int cursor;
struct cell *cells;
uint32_t foreground;
uint32_t background;
bool dirty;
bool all_dirty;
};
struct vt {
int state; /* enum state */
struct {
struct {
unsigned value;
struct {
unsigned value[16];
size_t idx;
} sub;
} v[16];
size_t idx;
} params;
struct {
uint8_t data[2];
size_t idx;
} intermediates;
struct {
uint8_t data[1024];
size_t idx;
} osc;
struct {
uint8_t data[4];
size_t idx;
size_t left;
} utf8;
bool bold;
bool dim;
bool italic;
bool underline;
bool strikethrough;
bool blink;
bool conceal;
bool reverse;
uint32_t foreground;
uint32_t background;
};
struct terminal {
struct vt vt;
struct grid grid;
};

314
vt.c Normal file
View file

@ -0,0 +1,314 @@
#include "vt.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define LOG_MODULE "vt"
#define LOG_ENABLE_DBG 1
#include "log.h"
#include "csi.h"
#include "osc.h"
/* https://vt100.net/emu/dec_ansi_parser */
enum state {
STATE_SAME, /* For state_transition */
STATE_ANYWHERE,
STATE_ESCAPE,
STATE_GROUND,
STATE_CSIENTRY,
STATE_CSIPARAM,
STATE_OSCSTRING,
STATE_UTF8,
};
enum action {
ACTION_NONE, /* For state_transition */
ACTION_IGNORE,
ACTION_CLEAR,
ACTION_EXECUTE,
ACTION_PRINT,
ACTION_PARAM,
ACTION_COLLECT,
ACTION_CSIDISPATCH,
ACTION_OSCSTART,
ACTION_OSCEND,
ACTION_OSCPUT,
ACTION_UTF8,
};
static const char *const state_names[] = {
[STATE_SAME] = "no change",
[STATE_ANYWHERE] = "anywhere",
[STATE_ESCAPE] = "escape",
[STATE_GROUND] = "ground",
[STATE_CSIENTRY] = "CSI entry",
[STATE_CSIPARAM] = "CSI param",
[STATE_OSCSTRING] = "OSC string",
[STATE_UTF8] = "UTF-8",
};
static const char *const action_names[] __attribute__((unused)) = {
[ACTION_NONE] = "no action",
[ACTION_IGNORE] = "ignore",
[ACTION_CLEAR] = "clear",
[ACTION_EXECUTE] = "execute",
[ACTION_PRINT] = "print",
[ACTION_PARAM] = "param",
[ACTION_COLLECT] = "collect",
[ACTION_CSIDISPATCH] = "CSI dispatch",
[ACTION_OSCSTART] = "OSC start",
[ACTION_OSCEND] = "OSC end",
[ACTION_OSCPUT] = "OSC put",
[ACTION_UTF8] = "begin UTF-8",
};
struct state_transition {
enum action action;
enum state state;
};
static const struct state_transition state_anywhere[256] = {
[0x1b] = {.state = STATE_ESCAPE},
};
static const struct state_transition state_ground[256] = {
[0x00 ... 0x17] = {.action = ACTION_EXECUTE},
[0x20 ... 0x7f] = {.action = ACTION_PRINT},
[0xc2 ... 0xdf] = {.action = ACTION_UTF8, .state = STATE_UTF8}, /* 2 chars */
[0xe0 ... 0xef] = {.action = ACTION_UTF8, .state = STATE_UTF8}, /* 3 chars */
[0xf0 ... 0xf4] = {.action = ACTION_UTF8, .state = STATE_UTF8}, /* 4 chars */
};
static const struct state_transition state_escape[256] = {
[0x5b] = {.state = STATE_CSIENTRY},
[0x5d] = {.state = STATE_OSCSTRING},
};
static const struct state_transition state_csientry[256] = {
[0x30 ... 0x39] = {.action = ACTION_PARAM, .state = STATE_CSIPARAM},
[0x3b] = {.action = ACTION_PARAM, .state = STATE_CSIPARAM},
[0x3c ... 0x3f] = {.action = ACTION_COLLECT, .state = STATE_CSIPARAM},
[0x40 ... 0x7e] = {.action = ACTION_CSIDISPATCH, .state = STATE_GROUND},
[0x6d] = {.action = ACTION_CSIDISPATCH, .state = STATE_GROUND},
};
static const struct state_transition state_csiparam[256] = {
[0x30 ... 0x39] = {.action = ACTION_PARAM},
[0x3b] = {.action = ACTION_PARAM},
[0x40 ... 0x7e] = {.action = ACTION_CSIDISPATCH, .state = STATE_GROUND},
[0x6d] = {.action = ACTION_CSIDISPATCH, .state = STATE_GROUND},
};
static const struct state_transition state_ocsstring[256] = {
[0x07] = {.state = STATE_GROUND}, /* Not in diagram */
[0x20 ... 0x7f] = {.action = ACTION_OSCPUT},
};
static const struct state_transition* states[] = {
[STATE_ANYWHERE] = state_anywhere,
[STATE_ESCAPE] = state_escape,
[STATE_GROUND] = state_ground,
[STATE_CSIENTRY] = state_csientry,
[STATE_CSIPARAM] = state_csiparam,
[STATE_OSCSTRING] = state_ocsstring,
};
static const enum action entry_actions[] = {
[STATE_SAME] = ACTION_NONE,
[STATE_ANYWHERE] = ACTION_NONE,
[STATE_ESCAPE] = ACTION_NONE,
[STATE_GROUND] = ACTION_NONE,
[STATE_CSIENTRY] = ACTION_CLEAR,
[STATE_CSIPARAM] = ACTION_NONE,
[STATE_OSCSTRING] = ACTION_OSCSTART,
[STATE_UTF8] = ACTION_NONE,
};
static const enum action exit_actions[] = {
[STATE_SAME] = ACTION_NONE,
[STATE_ANYWHERE] = ACTION_NONE,
[STATE_ESCAPE] = ACTION_NONE,
[STATE_GROUND] = ACTION_NONE,
[STATE_CSIENTRY] = ACTION_NONE,
[STATE_CSIPARAM] = ACTION_NONE,
[STATE_OSCSTRING] = ACTION_OSCEND,
[STATE_UTF8] = ACTION_NONE,
};
static bool
action(struct terminal *term, enum action action, uint8_t c)
{
switch (action) {
case ACTION_NONE:
break;
case ACTION_IGNORE:
break;
case ACTION_EXECUTE:
LOG_DBG("execute: 0x%02x", c);
switch (c) {
case '\r':
term->grid.cells[term->grid.cursor].dirty = true;
term->grid.cursor = term->grid.cursor / term->grid.cols * term->grid.cols;
term->grid.cells[term->grid.cursor].dirty = true;
break;
}
return true;
case ACTION_CLEAR:
memset(&term->vt.params, 0, sizeof(term->vt.params));
memset(&term->vt.intermediates, 0, sizeof(term->vt.intermediates));
memset(&term->vt.osc, 0, sizeof(term->vt.osc));
memset(&term->vt.utf8, 0, sizeof(term->vt.utf8));
break;
case ACTION_PRINT:{
struct cell *cell = &term->grid.cells[term->grid.cursor];
cell->dirty = true;
if (term->vt.utf8.idx > 0) {
LOG_DBG("print: UTF8: %.*s", (int)term->vt.utf8.idx, term->vt.utf8.data);
memcpy(cell->c, term->vt.utf8.data, term->vt.utf8.idx);
cell->c[term->vt.utf8.idx] = '\0';
} else {
LOG_DBG("print: ASCII: %c", c);
cell->c[0] = c;
cell->c[1] = '\0';
}
cell->foreground = term->vt.foreground;
cell->background = term->vt.background;
term->grid.cells[++term->grid.cursor].dirty = true;
term->grid.dirty = true;
break;
}
case ACTION_PARAM:{
if (term->vt.params.idx == 0)
term->vt.params.idx = 1;
if (c == ';') {
term->vt.params.idx++;
} else if (c == ':') {
if (term->vt.params.v[term->vt.params.idx - 1].sub.idx == 0)
term->vt.params.v[term->vt.params.idx - 1].sub.idx = 1;
} else {
if (term->vt.params.v[term->vt.params.idx - 1].sub.idx > 0)
term->vt.params.v[term->vt.params.idx - 1].sub.value[term->vt.params.v[term->vt.params.idx - 1].sub.idx] *= 10;
else
term->vt.params.v[term->vt.params.idx - 1].value *= 10;
if (term->vt.params.v[term->vt.params.idx - 1].sub.idx > 0)
term->vt.params.v[term->vt.params.idx - 1].sub.value[term->vt.params.v[term->vt.params.idx - 1].sub.idx] += c - '0';
else
term->vt.params.v[term->vt.params.idx - 1].value += c - '0';
}
break;
}
case ACTION_COLLECT:
LOG_DBG("collect");
term->vt.intermediates.data[term->vt.intermediates.idx++] = c;
break;
case ACTION_CSIDISPATCH:
return csi_dispatch(term, c);
case ACTION_OSCSTART:
term->vt.osc.idx = 0;
break;
case ACTION_OSCPUT:
term->vt.osc.data[term->vt.osc.idx++] = c;
break;
case ACTION_OSCEND:
return osc_dispatch(term);
case ACTION_UTF8:
term->vt.utf8.idx = 0;
if (c >= 0x2c && c <= 0xdf)
term->vt.utf8.left = 2;
else if (c >= 0xe0 && c <= 0xef)
term->vt.utf8.left = 3;
else
term->vt.utf8.left = 4;
term->vt.utf8.data[term->vt.utf8.idx++] = c;
term->vt.utf8.left--;
break;
}
return true;
}
static bool
process_utf8(struct terminal *term, uint8_t c)
{
LOG_DBG("UTF-8: 0x%02x", c);
term->vt.utf8.data[term->vt.utf8.idx++] = c;
term->vt.utf8.left--;
if (term->vt.utf8.left == 0)
term->vt.state = STATE_GROUND;
return true;
}
void
vt_from_slave(struct terminal *term, const uint8_t *data, size_t len)
{
//int cursor = term->grid.cursor;
for (size_t i = 0; i < len; i++) {
//LOG_DBG("input: 0x%02x", data[i]);
enum state current_state = term->vt.state;
const struct state_transition *transition = &state_anywhere[data[i]];
if (transition->action == ACTION_NONE && transition->state == STATE_SAME) {
if (current_state == STATE_UTF8) {
if (!process_utf8(term, data[i]))
abort();
if (current_state == STATE_UTF8)
continue;
if (!action(term, ACTION_PRINT, 0))
abort();
continue;
}
transition = &states[current_state][data[i]];
if (transition->action == ACTION_NONE && transition->state == STATE_SAME) {
LOG_ERR("unimplemented transition from %s: 0x%02x",
state_names[current_state], data[i]);
abort();
}
}
if (transition->state != STATE_SAME) {
enum action exit_action = exit_actions[current_state];
if (exit_action != ACTION_NONE && !action(term, exit_action, data[i]))
abort();
}
if (!action(term, transition->action, data[i]))
abort();
if (transition->state != STATE_SAME) {
LOG_DBG("transition: %s -> %s", state_names[current_state],
state_names[transition->state]);
term->vt.state = transition->state;
enum action entry_action = entry_actions[transition->state];
if (entry_action != ACTION_NONE && !action(term, entry_action, data[i]))
abort();
}
}
}

8
vt.h Normal file
View file

@ -0,0 +1,8 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
#include "terminal.h"
void vt_from_slave(struct terminal *term, const uint8_t *data, size_t len);