mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-04 04:06:06 -05:00
wip: vt parsing: initial csi/osc dispatching
This commit is contained in:
parent
0e6aa61d69
commit
2a4c08b941
10 changed files with 571 additions and 103 deletions
87
csi.c
Normal file
87
csi.c
Normal 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
6
csi.h
Normal 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
162
main.c
|
|
@ -8,6 +8,9 @@
|
|||
#include <locale.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
//#include <termios.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <xdg-shell.h>
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
12
osc.c
Normal file
12
osc.c
Normal 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
6
osc.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "terminal.h"
|
||||
|
||||
bool osc_dispatch(struct terminal *term);
|
||||
5
slave.c
5
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)
|
||||
|
|
|
|||
70
terminal.h
Normal file
70
terminal.h
Normal 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
314
vt.c
Normal 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
8
vt.h
Normal 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);
|
||||
Loading…
Add table
Add a link
Reference in a new issue