123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>
- #include "term.h"
- #define MAX_PARAMS 2
- static char *colors[8] = {
- "000000",
- "ff0000",
- "00ff00",
- "ffff00",
- "0000ff",
- "ff00ff",
- "00ffff",
- "ffffff",
- };
- void term_init(struct term *t, int num_rows, int num_cols, int pango)
- {
- t->state = TERM_STATE_GROUND;
- t->cursor.x = 0;
- t->cursor.y = 0;
- t->num_rows = num_rows;
- t->num_cols = num_cols;
- t->rows = calloc(t->num_rows, sizeof(struct row));
- t->param_idx = 0;
- t->params[0] = 0;
- t->params[1] = 0;
- t->pango = pango;
- t->sgra.normal = 1;
- for (int i = 0; i < t->num_rows; i++) {
- t->rows[i].cells = calloc(t->num_cols, sizeof(struct cell));
- for (int j = 0; j < t->num_cols; j++) {
- struct cell *cur_cell = &t->rows[i].cells[j];
- cur_cell->c = ' ';
- cur_cell->sgra.normal = 1;
- }
- }
- }
- static void term_cursor_move_up(struct term *t, int off) {
- t->cursor.y -= off;
- if (t->cursor.y < 0) {
- t->cursor.y = 0;
- }
- }
- static void term_cursor_move_left(struct term *t, int off) {
- t->cursor.x -= off;
- if (t->cursor.x < 0) {
- t->cursor.x = 0;
- }
- }
- static void term_cursor_move_down(struct term *t, int off) {
- t->cursor.y += off;
- if (t->cursor.y > t->num_rows - 1) {
- t->cursor.y = t->num_rows - 1;
- }
- }
- static void term_cursor_move_right(struct term *t, int off) {
- t->cursor.x += off;
- if (t->cursor.x > t->num_cols - 1) {
- t->cursor.x = t->num_cols - 1;
- }
- }
- static void term_line_feed(struct term *t)
- {
- if (t->cursor.y == t->num_rows - 1) {
- for (int i = 1; i < t->num_rows; i++) {
- memcpy(t->rows[i-1].cells, t->rows[i].cells, t->num_cols * sizeof(struct cell));
- }
- for (int i = 0; i < t->num_cols; i++) {
- t->rows[t->cursor.y].cells[i].c = ' ';
- }
- } else {
- t->cursor.y++;
- }
- }
- static void term_print(struct term *t, char c) {
- struct row *cur_row = &t->rows[t->cursor.y];
- struct cell *cur_cell = &cur_row->cells[t->cursor.x];
- cur_cell->c = c;
- cur_cell->sgra = t->sgra;
- if (t->cursor.x >= t->num_cols - 1) {
- term_line_feed(t);
- t->cursor.x = 0;
- } else {
- t->cursor.x++;
- }
- }
- static void term_clear(struct term *t) {
- t->param_idx = 0;
- t->params[0] = 0;
- t->params[1] = 0;
- // TODO: also clear:
- // private flag?
- // intermediate characters?
- // final character?
- }
- static void term_add_param_digit(struct term *t, char c) {
- t->params[t->param_idx] *= 10;
- t->params[t->param_idx] += c - '0';
- }
- static void term_next_param(struct term *t) {
- if (t->param_idx > TERM_MAX_PARAMS - 1)
- return;
- t->params[++t->param_idx] = 0;
- }
- static void term_param(struct term *t, char c) {
- switch (c) {
- case 0x30 ... 0x39: term_add_param_digit(t, c); break;
- case 0x3b: term_next_param(t); break;
- }
- }
- static void term_execute(struct term *t, char c) {
- switch (c) {
- case '\b': term_cursor_move_left(t, 1); break;
- case '\f':
- case '\n':
- case '\v':
- term_line_feed(t); break;
- case '\r': t->cursor.x = 0; break;
- case '\t':
- term_print(t, ' ');
- term_print(t, ' ');
- term_print(t, ' ');
- term_print(t, ' ');
- break;
- }
- }
- static void term_collect(struct term *t, char c) {
- // not implemented
- }
- static void term_csi_erase_in_display(struct term *t) {
- // inclusive
- int start_x;
- int start_y;
- // exclusive
- int end_x;
- int end_y;
- switch (t->params[0]) {
- case 0:
- start_x = t->cursor.x;
- start_y = t->cursor.y;
- end_x = t->num_cols;
- end_y = t->num_rows;
- break;
- case 1:
- start_x = 0;
- start_y = 0;
- end_x = t->cursor.x + 1;
- end_y = t->cursor.y + 1;
- break;
- // We don't have a scrollback buffer, so 2 and 3 are equivalent
- case 2:
- case 3:
- start_x = 0;
- start_y = 0;
- end_x = t->num_cols;
- end_y = t->num_rows;
- break;
- default: return;
- }
- for (int i = start_y; i < end_y; i++) {
- for (int j = start_x; j < end_x; j++) {
- struct cell *cur_cell = &t->rows[i].cells[j];
- cur_cell->c = ' ';
- cur_cell->sgra.normal = 1;
- }
- }
- }
- static void term_csi_erase_in_line(struct term *t) {
- int start_x; // inclusive
- int end_x; // exclusive
- switch (t->params[0]) {
- case 0:
- start_x = t->cursor.x;
- end_x = t->num_cols;
- break;
- case 1:
- start_x = 0;
- end_x = t->cursor.x + 1;
- break;
- case 2:
- start_x = 0;
- end_x = t->num_cols;
- break;
- }
- for (int j = start_x; j < end_x; j++) {
- struct cell *cur_cell = &t->rows[t->cursor.y].cells[j];
- cur_cell->c = ' ';
- cur_cell->sgra.normal = 1;
- }
- }
- static void term_csi_sgr(struct term *t) {
- for (int i = 0; i < t->param_idx + 1; i++) {
- int p = t->params[i];
- if (0 == p) {
- t->sgra.normal = 1;
- } else {
- t->sgra.normal = 0;
- switch (p) {
- case 1: t->sgra.bold = 1; break;
- case 30 ... 37: t->sgra.fg = p - 30; break;
- case 40 ... 47: t->sgra.bg = p - 40; break;
- }
- }
- }
- }
- static void term_csi_dispatch(struct term *t, char c) {
- int p0 = t->params[0];
- int p1 = t->params[1];
- switch (c) {
- case 'A': term_cursor_move_up(t, p0 == 0 ? 1 : p0); break;
-
- case 'B': term_cursor_move_down(t, p0 == 0 ? 1 : p0); break;
-
- case 'C': term_cursor_move_right(t, p0 == 0 ? 1 : p0); break;
-
- case 'D': term_cursor_move_left(t, p0 == 0 ? 1 : p0); break;
- case 'E': t->cursor.x = 0; term_cursor_move_down(t, p0 == 0 ? 1 : p0); break;
-
- case 'F': t->cursor.x = 0; term_cursor_move_up(t, p0 == 0 ? 1 : p0); break;
- case 'G': t->cursor.x = p0; break;
- case 'H': t->cursor.y = p0 == 0 ? 0 : p0 - 1; t->cursor.x = p1 == 0 ? 0 : p1 - 1; break;
- case 'J': term_csi_erase_in_display(t); break;
-
- case 'K': term_csi_erase_in_line(t); break;
- case 'm': term_csi_sgr(t); break;
- }
- }
- static void term_esc_dispatch(struct term *t, char c) {
- switch (c) {
- case 'D': term_line_feed(t); break;
- case 'E': t->cursor.x = 0; term_line_feed(t); break;
- }
- }
- void term_putc(struct term *t, char c)
- {
- // Loosely based on a very (very) limited subset of the state machine at https://vt100.net/emu/dec_ansi_parser
- // Expect most things to be unsupported, irrelevant or straight out broken ^__-
- switch (t->state) {
- case TERM_STATE_GROUND:
- // events
- switch (c) {
- case 0x00 ... 0x17:
- case 0x19:
- case 0x1c ... 0x1f: term_execute(t, c); break;
- case 0x1b: t->state = TERM_STATE_ESC; break;
- case 0x20 ... 0x7f: term_print(t, c); break;
- } break;
- case TERM_STATE_ESC:
- // entry
- term_clear(t);
- // events
- switch (c) {
- case 0x00 ... 0x17:
- case 0x19:
- case 0x1c ... 0x1f: term_execute(t, c); break;
- case 0x20 ... 0x2f: term_collect(t, c); t->state = TERM_STATE_ESC_INTERMEDIATE; break;
- case 0x30 ... 0x4f:
- case 0x51 ... 0x57:
- case 0x59:
- case 0x5a:
- case 0x5c:
- case 0x60 ... 0x7e: term_esc_dispatch(t, c); t->state = TERM_STATE_GROUND; break;
- case 0x5b: t->state = TERM_STATE_CSI_ENTRY; break;
- } break;
- case TERM_STATE_ESC_INTERMEDIATE:
- switch (c) {
- case 0x00 ... 0x17:
- case 0x19:
- case 0x1c ... 0x1f: term_execute(t, c); break;
- case 0x1b: t->state = TERM_STATE_ESC; break;
- case 0x20 ... 0x2f: term_collect(t, c); break;
- case 0x30 ... 0x7e: term_esc_dispatch(t, c); t->state = TERM_STATE_GROUND; break;
- } break;
- case TERM_STATE_CSI_ENTRY:
- // entry
- term_clear(t);
- // events
- switch (c) {
- case 0x00 ... 0x17:
- case 0x19:
- case 0x1c ... 0x1f: term_execute(t, c); break;
- case 0x1b: t->state = TERM_STATE_ESC; break;
- case 0x20 ... 0x2f: term_collect(t, c); t->state = TERM_STATE_CSI_INTERMEDIATE; break;
- case 0x3a: t->state = TERM_STATE_CSI_IGNORE; break;
- case 0x3c ... 0x3f: term_collect(t, c); break;
- case 0x30 ... 0x39:
- case 0x3b:
- term_param(t, c); t->state = TERM_STATE_CSI_PARAM; break;
- case 0x40 ... 0x7e: term_csi_dispatch(t, c); t->state = TERM_STATE_GROUND; break;
- } break;
- case TERM_STATE_CSI_PARAM:
- // events
- switch (c) {
- case 0x00 ... 0x17:
- case 0x19:
- case 0x1c ... 0x1f: term_execute(t, c); break;
- case 0x1b: t->state = TERM_STATE_ESC; break;
- case 0x20 ... 0x2f: term_collect(t, c); t->state = TERM_STATE_CSI_INTERMEDIATE; break;
- case 0x30 ... 0x39:
- case 0x3b:
- term_param(t, c); break;
- case 0x3a:
- case 0x3c ... 0x3f: t->state = TERM_STATE_CSI_IGNORE; break;
-
- case 0x40 ... 0x7e: term_csi_dispatch(t, c); t->state = TERM_STATE_GROUND; break;
- } break;
- case TERM_STATE_CSI_INTERMEDIATE:
- // events
- switch (c) {
- case 0x00 ... 0x17:
- case 0x19:
- case 0x1c ... 0x1f: term_execute(t, c); break;
- case 0x1b: t->state = TERM_STATE_ESC; break;
- case 0x20 ... 0x2f: term_collect(t, c); break;
- case 0x30 ... 0x3f: t->state = TERM_STATE_CSI_IGNORE; break;
- case 0x40 ... 0x7e: term_csi_dispatch(t, c); t->state = TERM_STATE_GROUND; break;
- } break;
- case TERM_STATE_CSI_IGNORE:
- // events
- switch (c) {
- case 0x00 ... 0x17:
- case 0x19:
- case 0x1c ... 0x1f: term_execute(t, c); break;
- case 0x1b: t->state = TERM_STATE_ESC; break;
- case 0x40 ... 0x7e: t->state = TERM_STATE_GROUND; break;
- } break;
- }
- }
- void term_put_string(struct term *t, char *str) {
- int len = strlen(str);
- for (int i = 0; i < len; i++) {
- term_putc(t, str[i]);
- }
- }
- static char *xml_entity(char *str, char c) {
- switch (c) {
- case '<':
- strncpy(str, "<", 9);
- break;
-
- case '>':
- strncpy(str, ">", 9);
- break;
- default:
- str[0] = c;
- str[1] = '\0';
- break;
- }
- return str;
- }
- char *term_to_string(struct term *t)
- {
- // TODO: Allocate once
- // TODO: don't allocate too much if pango is not used.
- int str_size = t->num_rows * t->num_cols * 128 + t->num_rows;
- char *ret = calloc(str_size, sizeof(char));
- char *s = ret;
- char cell_str[9];
- for (int i = 0; i < t->num_rows; i++) {
- for (int j = 0; j < t->num_cols; j++) {
- struct cell cur_cell = t->rows[i].cells[j];
- if (t->pango && 0 == cur_cell.sgra.normal) {
- // TODO: use snprintf...
- s += sprintf(s, "<span weight='%s' fgcolor='#%s' bgcolor='#%s'>%s</span>",
- cur_cell.sgra.bold ? "bold" : "normal",
- colors[cur_cell.sgra.fg],
- colors[cur_cell.sgra.bg],
- xml_entity(cell_str, cur_cell.c));
- } else {
- *s++ = cur_cell.c;
- }
- }
- if (i == t->num_rows - 1) {
- *s++ = '\0';
- } else {
- *s++ = '\n';
- }
- }
- return ret;
- }
- void term_free(struct term *t)
- {
- if (t->rows != NULL) {
- for(int i = 0; i < t->num_rows; i++) {
- if (t->rows[i].cells != NULL) {
- free(t->rows[i].cells);
- }
- }
- free(t->rows);
- }
- }
|