|
@@ -4,82 +4,252 @@
|
|
|
|
|
|
#include "term.h"
|
|
|
|
|
|
-void term_init(struct term *t, int rows, int cols)
|
|
|
+#define MAX_PARAMS 2
|
|
|
+
|
|
|
+void term_init(struct term *t, int num_rows, int num_cols)
|
|
|
{
|
|
|
- t->state = TERM_STATE_NORMAL;
|
|
|
+ t->state = TERM_STATE_GROUND;
|
|
|
t->cursor.x = 0;
|
|
|
t->cursor.y = 0;
|
|
|
- t->rows = rows;
|
|
|
- t->cols = cols;
|
|
|
- t->lines = calloc(t->rows, sizeof(char *));
|
|
|
- for (int i = 0; i < t->rows; i++) {
|
|
|
- t->lines[i] = calloc(t->cols, sizeof(char *));
|
|
|
- memset(t->lines[i], ' ', t->cols);
|
|
|
+ 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;
|
|
|
+ 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++) {
|
|
|
+ t->rows[i].cells[j].c = ' ';
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void term_put_string(struct term *t, char *str)
|
|
|
-{
|
|
|
- for (int i = 0; i < strlen(str); i++) {
|
|
|
- term_put(t, str[i]);
|
|
|
+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;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void term_cursor_inc_y(struct term *t)
|
|
|
+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->rows - 1) {
|
|
|
- for (int i = 1; i < t->rows; i++) {
|
|
|
- memcpy(t->lines[i-1], t->lines[i], t->cols);
|
|
|
+ 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);
|
|
|
}
|
|
|
- memset(t->lines[t->cursor.y], ' ', t->cols);
|
|
|
+ memset(t->rows[t->cursor.y].cells, ' ', t->num_cols);
|
|
|
} else {
|
|
|
t->cursor.y++;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void term_put(struct term *t, char c)
|
|
|
+static void term_print(struct term *t, char c) {
|
|
|
+ struct row *cur_row = &t->rows[t->cursor.y];
|
|
|
+
|
|
|
+ cur_row->cells[t->cursor.x].c = c;
|
|
|
+
|
|
|
+ if (t->cursor.x == t->num_cols - 1) {
|
|
|
+ term_cursor_move_down(t, 1);
|
|
|
+ t->cursor.x = 0;
|
|
|
+ cur_row->wrapped = true;
|
|
|
+ } else {
|
|
|
+ t->cursor.x++;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void term_clear(struct term *t) {
|
|
|
+ t->param_idx = 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 '\n': term_line_feed(t); break;
|
|
|
+
|
|
|
+ case '\r': t->cursor.x = 0; break;
|
|
|
+
|
|
|
+ case '\b': term_cursor_move_left(t, 1); break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void term_collect(struct term *t, char c) {
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+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); break;
|
|
|
+
|
|
|
+ case 'B': term_cursor_move_down(t, p0); break;
|
|
|
+
|
|
|
+ case 'C': term_cursor_move_right(t, p0); break;
|
|
|
+
|
|
|
+ case 'D': term_cursor_move_left(t, 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;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void term_putc(struct term *t, char c)
|
|
|
{
|
|
|
- if (t->state == TERM_STATE_NORMAL) {
|
|
|
- switch (c) {
|
|
|
- case '\n':
|
|
|
- term_cursor_inc_y(t);
|
|
|
- t->cursor.x = 0;
|
|
|
- break;
|
|
|
- case '\r':
|
|
|
- t->cursor.x = 0;
|
|
|
- break;
|
|
|
- case '\b':
|
|
|
- t->cursor.x--;
|
|
|
- break;
|
|
|
- case '\x1b':
|
|
|
- //t->state = TERM_STATE_ESC;
|
|
|
- break;
|
|
|
- default:
|
|
|
- t->lines[t->cursor.y][t->cursor.x] = c;
|
|
|
- if (t->cursor.x == t->cols - 1) {
|
|
|
- term_cursor_inc_y(t);
|
|
|
- t->cursor.x = 0;
|
|
|
- } else {
|
|
|
- t->cursor.x++;
|
|
|
- }
|
|
|
- }
|
|
|
- } else if (t->state == TERM_STATE_ESC) {
|
|
|
- switch (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;
|
|
|
+
|
|
|
+ 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 0x5b: t->state = TERM_STATE_CSI_ENTRY; break;
|
|
|
+
|
|
|
+ default: t->state = TERM_STATE_GROUND; break;
|
|
|
+ } break;
|
|
|
+
|
|
|
+ case TERM_STATE_CSI_ENTRY:
|
|
|
+ // events
|
|
|
+ switch (c) {
|
|
|
+ case 0x00 ... 0x17:
|
|
|
+ case 0x19:
|
|
|
+ case 0x1c ... 0x1f: term_execute(t, c); break;
|
|
|
+
|
|
|
+ case 0x3c ... 0x3f: /* Ignore privates??? */ 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 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;
|
|
|
+ } break;
|
|
|
+
|
|
|
+ case TERM_STATE_CSI_INTERMEDIATE:
|
|
|
+ // events
|
|
|
+ switch (c) {
|
|
|
+ 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;
|
|
|
+
|
|
|
+ case TERM_STATE_CSI_IGNORE:
|
|
|
+ // events
|
|
|
+ switch (c) {
|
|
|
+ case 0x00 ... 0x17:
|
|
|
+ case 0x19:
|
|
|
+ case 0x1c ... 0x1f: term_execute(t, c); 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]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
char *term_to_string(struct term *t)
|
|
|
{
|
|
|
- int str_size = t->rows * t->cols + t->rows;
|
|
|
+ int str_size = t->num_rows * t->num_cols + t->num_rows;
|
|
|
char *str = calloc(str_size, sizeof(char));
|
|
|
int str_idx = 0;
|
|
|
|
|
|
- for (int i = 0; i < t->rows; i++) {
|
|
|
- for (int j = 0; j < t->cols && str_idx < str_size; j++) {
|
|
|
- str[str_idx++] = t->lines[i][j];
|
|
|
+ for (int i = 0; i < t->num_rows; i++) {
|
|
|
+ for (int j = 0; j < t->num_cols && str_idx < str_size; j++) {
|
|
|
+ str[str_idx++] = t->rows[i].cells[j].c;
|
|
|
}
|
|
|
- if (i == t->rows - 1) {
|
|
|
+ if (i == t->num_rows - 1) {
|
|
|
str[str_idx++] = '\0';
|
|
|
} else {
|
|
|
str[str_idx++] = '\n';
|
|
@@ -91,10 +261,12 @@ char *term_to_string(struct term *t)
|
|
|
|
|
|
void term_free(struct term *t)
|
|
|
{
|
|
|
- for(int i = 0; i < t->rows; i++) {
|
|
|
- if (t->lines[i] != NULL) {
|
|
|
- free(t->lines[i]);
|
|
|
+ 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);
|
|
|
}
|
|
|
- free(t->lines);
|
|
|
}
|