Selaa lähdekoodia

Escape states

Björn Åström 1 vuosi sitten
vanhempi
commit
02f1b41bb6
2 muutettua tiedostoa jossa 253 lisäystä ja 61 poistoa
  1. 226 54
      term.c
  2. 27 7
      term.h

+ 226 - 54
term.c

@@ -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);
 }

+ 27 - 7
term.h

@@ -1,21 +1,41 @@
 #ifndef __TERM_H
 #define __TERM_H
 
-#define TERM_STATE_NORMAL 0
-#define TERM_STATE_ESC 1
-#define TERM_STATE_CSI 2
+#include <stdbool.h>
+
+#define TERM_MAX_PARAMS 2
+
+enum term_state {
+	TERM_STATE_GROUND,
+	TERM_STATE_ESC,
+	TERM_STATE_CSI_ENTRY,
+	TERM_STATE_CSI_PARAM,
+	TERM_STATE_CSI_INTERMEDIATE,
+	TERM_STATE_CSI_IGNORE,
+};
 
 struct term_cursor {
 	int x;
 	int y;
 };
 
+struct cell {
+	char c;
+};
+
+struct row {
+	bool wrapped;
+	struct cell *cells;
+};
+
 struct term {
-	int state;
+	enum term_state state;
 	struct term_cursor cursor;
-	char **lines;
-	int rows;
-	int cols;
+	struct row *rows;
+	int num_rows;
+	int num_cols;
+	int params[TERM_MAX_PARAMS];
+	int param_idx;
 };
 
 void term_init(struct term *t, int rows, int cols);