From 2a4c08b94104a31a0944f4b0df6729029c1b55ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 15 Jun 2019 22:22:44 +0200 Subject: [PATCH] wip: vt parsing: initial csi/osc dispatching --- csi.c | 87 +++++++++++++++ csi.h | 6 + main.c | 162 ++++++++++----------------- meson.build | 4 + osc.c | 12 ++ osc.h | 6 + slave.c | 5 + terminal.h | 70 ++++++++++++ vt.c | 314 ++++++++++++++++++++++++++++++++++++++++++++++++++++ vt.h | 8 ++ 10 files changed, 571 insertions(+), 103 deletions(-) create mode 100644 csi.c create mode 100644 csi.h create mode 100644 osc.c create mode 100644 osc.h create mode 100644 terminal.h create mode 100644 vt.c create mode 100644 vt.h diff --git a/csi.c b/csi.c new file mode 100644 index 00000000..822be8e0 --- /dev/null +++ b/csi.c @@ -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; +} diff --git a/csi.h b/csi.h new file mode 100644 index 00000000..adf7b09c --- /dev/null +++ b/csi.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include "terminal.h" + +bool csi_dispatch(struct terminal *term, uint8_t final); diff --git a/main.c b/main.c index b682da37..195bbc71 100644 --- a/main.c +++ b/main.c @@ -8,6 +8,9 @@ #include #include +#include +//#include + #include #include @@ -18,6 +21,8 @@ #include "font.h" #include "shm.h" #include "slave.h" +#include "terminal.h" +#include "vt.h" static const uint32_t default_foreground = 0xffffffff; static const uint32_t default_background = 0x000000ff; @@ -33,29 +38,6 @@ struct wayland { 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 { bool quit; int ptmx; @@ -67,7 +49,8 @@ struct context { int height; struct wayland wl; - struct grid grid; + //struct grid grid; + struct terminal term; bool frame_is_scheduled; }; @@ -83,7 +66,7 @@ static const struct wl_callback_listener frame_listener = { static void grid_render(struct context *c) { - assert(c->grid.dirty); + assert(c->term.grid.dirty); assert(c->width > 0); assert(c->height > 0); @@ -95,7 +78,7 @@ grid_render(struct context *c) cairo_set_operator(buf->cairo, CAIRO_OPERATOR_SOURCE); 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; bg = (double)((default_background >> 16) & 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 col = 0; col < c->grid.cols; col++) { - int cell_idx = row * c->grid.cols + col; - struct cell *cell = &c->grid.cells[cell_idx]; + for (int row = 0; row < c->term.grid.rows; row++) { + for (int col = 0; col < c->term.grid.cols; col++) { + int cell_idx = row * c->term.grid.cols + col; + 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; 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 x_ofs = col * c->grid.cell_width; + int y_ofs = row * c->term.grid.cell_height + c->fextents.ascent; + int x_ofs = col * c->term.grid.cell_width; int damage_x = x_ofs; int damage_y = y_ofs - c->fextents.ascent; @@ -141,7 +124,7 @@ grid_render(struct context *c) cairo_rectangle( 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_glyph_t *glyphs = NULL; @@ -167,7 +150,7 @@ grid_render(struct context *c) wl_surface_damage_buffer( 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; 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 @@ -189,7 +172,7 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da c->frame_is_scheduled = false; wl_callback_destroy(wl_callback); - if (c->grid.dirty) + if (c->term.grid.dirty) grid_render(c); } @@ -202,25 +185,40 @@ resize(struct context *c, int width, int height) c->width = width; 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->grid.cell_height = (int)ceil(c->fextents.height); - c->grid.cols = c->width / c->grid.cell_width; - c->grid.rows = c->height / c->grid.cell_height; - c->grid.cells = realloc(c->grid.cells, - c->grid.cols * c->grid.rows * sizeof(c->grid.cells[0])); + c->term.grid.cell_width = (int)ceil(c->fextents.max_x_advance); + c->term.grid.cell_height = (int)ceil(c->fextents.height); + c->term.grid.cols = c->width / c->term.grid.cell_width; + c->term.grid.rows = c->height / c->term.grid.cell_height; + c->term.grid.cells = realloc(c->term.grid.cells, + 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++) { - c->grid.cells[i] = (struct cell){.foreground = default_foreground, + c->term.grid.cells[i] = (struct cell){.foreground = default_foreground, .background = default_background}; } 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) grid_render(c); @@ -378,8 +376,13 @@ main(int argc, const char *const *argv) struct context c = { .quit = false, .ptmx = posix_openpt(O_RDWR | O_NOCTTY), - .grid = {.foreground = default_foreground, - .background = default_background}, + .term = { + .vt = { + .state = 1, + }, + .grid = {.foreground = default_foreground, + .background = default_background}, + }, }; 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 */ const int default_width = 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); wl_display_dispatch_pending(c.wl.display); @@ -492,7 +495,7 @@ main(int argc, const char *const *argv) } if (fds[1].revents & POLLIN) { - char data[1024]; + uint8_t data[1024]; ssize_t count = read(c.ptmx, data, sizeof(data)); if (count < 0) { 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); - int new_cursor = c.grid.cursor; - for (int i = 0; i < count;) { - 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) + vt_from_slave(&c.term, data, count); + if (c.term.grid.dirty && !c.frame_is_scheduled) grid_render(&c); } @@ -578,7 +534,7 @@ out: if (c.wl.display != NULL) wl_display_disconnect(c.wl.display); - free(c.grid.cells); + free(c.term.grid.cells); if (c.font != NULL) cairo_scaled_font_destroy(c.font); diff --git a/meson.build b/meson.build index d78671e4..f1953050 100644 --- a/meson.build +++ b/meson.build @@ -57,12 +57,16 @@ endforeach executable( 'f00ter', + 'csi.c', 'csi.h', 'font.c', 'font.h', 'log.c', 'log.h', 'main.c', + 'osc.c', 'osc.h', 'shm.c', 'shm.h', 'slave.c', 'slave.h', + 'terminal.h', 'tllist.h', + 'vt.c', 'vt.h', wl_proto_src + wl_proto_headers, dependencies: [math, cairo, cairo_ft, fontconfig, wayland_client, wayland_cursor, xkb], install: true) diff --git a/osc.c b/osc.c new file mode 100644 index 00000000..5f71287e --- /dev/null +++ b/osc.c @@ -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; +} diff --git a/osc.h b/osc.h new file mode 100644 index 00000000..91a01bc4 --- /dev/null +++ b/osc.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include "terminal.h" + +bool osc_dispatch(struct terminal *term); diff --git a/slave.c b/slave.c index d913564b..922cd714 100644 --- a/slave.c +++ b/slave.c @@ -52,6 +52,10 @@ slave_spawn(int ptmx) close(pts); pts = -1; + execl("/usr/bin/zsh", "/usr/bin/zsh", NULL); + +#if 0 + /* TODO: exec() */ sleep(1); const char *s = "hello\tbla\nfoobar\thaa"; @@ -64,6 +68,7 @@ slave_spawn(int ptmx) write(STDOUT_FILENO, "\råäö", strlen("\råäö")); sleep(1000); +#endif err: if (pts != -1) diff --git a/terminal.h b/terminal.h new file mode 100644 index 00000000..08e97824 --- /dev/null +++ b/terminal.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include + +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; +}; diff --git a/vt.c b/vt.c new file mode 100644 index 00000000..870bc029 --- /dev/null +++ b/vt.c @@ -0,0 +1,314 @@ +#include "vt.h" + +#include +#include +#include + +#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(); + } + } +} diff --git a/vt.h b/vt.h new file mode 100644 index 00000000..8652e341 --- /dev/null +++ b/vt.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include + +#include "terminal.h" + +void vt_from_slave(struct terminal *term, const uint8_t *data, size_t len);