commit 794f8d0

uint  ·  2026-05-13 22:01:04 +0000 UTC
parent e684149
add escape sequence, csi support
3 files changed,  +239, -11
+1, -1
1@@ -116,7 +116,7 @@ static void init(void)
2 		die(EXIT_FAILURE, "failed to fork pty");
3 	if (term.ptypid == 0) {
4 		/* TODO: change later */
5-		setenv("TERM", "dumb", 1);
6+		setenv("TERM", "xterm-256color", 1);
7 		execlp("/bin/sh", "sh", "-i", NULL);
8 		_exit(127);
9 	}
+11, -0
 1@@ -7,6 +7,13 @@
 2 
 3 /* resolve 1D rune from 2D input */ 
 4 #define RUNE(t, x, y) ((t)->runes[(y) * (t)->cols + (x)])
 5+#define CSI_PARAMS_MAX (16)
 6+
 7+enum TState {
 8+	TSTATE_NORMAL,
 9+	TSTATE_ESC,
10+	TSTATE_CSI,
11+};
12 
13 typedef struct {
14 	uint32_t       cp;  /* codepoint */
15@@ -30,6 +37,10 @@ typedef struct {
16 
17 	int            ptyfd;
18 	pid_t          ptypid;
19+
20+	enum TState   state;
21+	int           csi_params[CSI_PARAMS_MAX];
22+	int           csi_param;
23 } Term;
24 
25 /**
+227, -10
  1@@ -5,6 +5,196 @@
  2 
  3 #include "term.h"
  4 
  5+static int csi_arg(Term* t, int i, int fallback);
  6+static void csi_dispatch(Term* t, Caret* c, unsigned char ch);
  7+static void csi_reset(Term* t);
  8+static void erase_display(Term* t, Caret* c);
  9+static void erase_line(Term* t, Caret* c);
 10+static void reset_rune(Term* t, int x, int y);
 11+
 12+/** TODO
 13+ */
 14+static int csi_arg(Term* t, int i, int fallback)
 15+{
 16+	if (i > t->csi_param)
 17+		return fallback;
 18+	if (t->csi_params[i] == 0)
 19+		return fallback;
 20+
 21+	return t->csi_params[i];
 22+}
 23+
 24+/** TODO
 25+ */
 26+static void csi_dispatch(Term* t, Caret* c, unsigned char ch)
 27+{
 28+	switch (ch) {
 29+	case 'H': /* CUP - Cursor Position - ESC[row;colH */
 30+	case 'f': { /* HVP - Horizontal and Vertical Postion - ESC[row;colf */
 31+		int row = csi_arg(t, 0, 1) - 1;
 32+		int col = csi_arg(t, 1, 1) - 1;
 33+
 34+		if (row < 0)
 35+			row = 0;
 36+		if (col < 0)
 37+			col = 0;
 38+		if (row >= t->rows)
 39+			row = t->rows - 1;
 40+		if (col >= t->cols)
 41+			col = t->cols - 1;
 42+
 43+		c->y = row;
 44+		c->x = col;
 45+		break;
 46+	}
 47+
 48+	case 'J': /* ED - Erase in Display - ESC[nJ */
 49+		erase_display(t, c);
 50+		break;
 51+
 52+	case 'K': /* EL - Erase in Line - ESC[nK */
 53+		erase_line(t, c);
 54+		break;
 55+
 56+	case 'm': /* SGR - Select Graphics Rendition - ESC[nm, colours */
 57+		/* TODO: colours */
 58+		break;
 59+
 60+	case 'A': { /* CUU - cursor up */
 61+		int n = csi_arg(t, 0, 1);
 62+		c->y -= n;
 63+		if (c->y < 0)
 64+			c->y = 0;
 65+		break;
 66+	}
 67+
 68+	case 'B': { /* CUD - cursor down */
 69+		int n = csi_arg(t, 0, 1);
 70+		c->y += n;
 71+		if (c->y >= t->rows)
 72+			c->y = t->rows - 1;
 73+		break;
 74+	}
 75+
 76+	case 'C': { /* CUF - cursor forward */
 77+		int n = csi_arg(t, 0, 1);
 78+		c->x += n;
 79+		if (c->x >= t->cols)
 80+			c->x = t->cols - 1;
 81+		break;
 82+	}
 83+
 84+	case 'D': { /* CUB - cursor back */
 85+			  int n = csi_arg(t, 0, 1);
 86+			  c->x -= n;
 87+			  if (c->x < 0)
 88+				  c->x = 0;
 89+			  break;
 90+	}
 91+
 92+	case 'G': { /* CHA - cursor horizontal absolute */
 93+			  int col = csi_arg(t, 0, 1) - 1;
 94+
 95+			  if (col < 0)
 96+				  col = 0;
 97+			  if (col >= t->cols)
 98+				  col = t->cols - 1;
 99+
100+			  c->x = col;
101+			  break;
102+	}
103+
104+	case 'd': { /* VPA - vertical position absolute */
105+			  int row = csi_arg(t, 0, 1) - 1;
106+
107+			  if (row < 0)
108+				  row = 0;
109+			  if (row >= t->rows)
110+				  row = t->rows - 1;
111+
112+			  c->y = row;
113+			  break;
114+	}
115+
116+	/* TODO? unsupported */
117+	case 'h': /* set mode */
118+	case 'l': /* reset mode */
119+	case 'r': /* scrolling region */
120+	case 's': /* save cursor */
121+	case 'u': /* restore cursor */
122+		break;
123+
124+	default: /* Unsupported CSI */
125+		break;
126+	}
127+}
128+
129+/**
130+ * @brief erase CSI parameters
131+ *
132+ * @param t term instance to reset
133+ */
134+static void csi_reset(Term* t)
135+{
136+	for (int i = 0; i < CSI_PARAMS_MAX; i++)
137+		t->csi_params[i] = 0;
138+	t->csi_param = 0;
139+}
140+
141+/** TODO
142+ */
143+static void erase_display(Term* t, Caret* c)
144+{
145+	int mode = csi_arg(t, 0, 0);
146+
147+	/* mode 2: whole screen */
148+	if (mode == 2 || mode == 3) {
149+		for (int y = 0; y < t->rows; y++) {
150+			for (int x = 0; x < t->cols; x++)
151+				reset_rune(t, x, y);
152+		}
153+		return;
154+	}
155+
156+	/* mode 0: cursor to end of screen */
157+	if (mode == 0) {
158+		for (int y = c->y; y < t->rows; y++) {
159+			int x0 = (y == c->y) ? c->x : 0;
160+			for (int x = x0; x < t->cols; x++)
161+				reset_rune(t, x, y);
162+		}
163+		return;
164+	}
165+
166+	/* mode 1: start of screen to cursor */
167+	if (mode == 1) {
168+		for (int y = 0; y <= c->y; y++) {
169+			int x1 = (y == c->y) ? c->x : t->cols - 1;
170+			for (int x = 0; x <= x1; x++)
171+				reset_rune(t, x, y);
172+		}
173+	}
174+}
175+
176+/** TODO
177+ */
178+static void erase_line(Term* t, Caret* c)
179+{
180+	for (int x = c->x; x < t->cols; x++)
181+		reset_rune(t, x, c->y);
182+}
183+
184+/** TODO
185+ */
186+static void reset_rune(Term* t, int x, int y)
187+{
188+	Rune* r = &(RUNE(t, x, y));
189+	r->cp = ' ';
190+	r->fg = t->fg;
191+	r->bg = t->bg;
192+	r->dmg = true;
193+}
194+
195 void term_clear(Term* t, Caret* c)
196 {
197 	for (int y = 0; y < t->rows; y++) {
198@@ -81,20 +271,47 @@ void term_scroll(Term* t)
199 
200 void term_write(Term* t, Caret* c, const char* s, size_t n)
201 {
202-	size_t off = 0;
203+	for (size_t i = 0; i < n; i++) {
204+		unsigned char ch = (unsigned char)s[i];
205 
206-	while (off < n) {
207-		uint32_t cp;
208-		/* s+off: pointer to current position
209-		 * n-off: remaining bytes
210-		 */
211-		size_t len = grapheme_decode_utf8(s+off, n-off, &cp);
212+		switch (t->state) {
213+		case TSTATE_NORMAL:
214+			if (ch == '\x1B') {
215+				t->state = TSTATE_ESC;
216+			}
217+			else {
218+				term_putc(t, c, ch);
219+			}
220+			break;
221 
222-		if (len ==0)
223+		case TSTATE_ESC:
224+			if (ch == '[') {
225+				csi_reset(t);
226+				t->state = TSTATE_CSI;
227+			}
228+			else {
229+				t->state = TSTATE_NORMAL;
230+			}
231 			break;
232 
233-		term_putc(t, c, cp);
234-		off += len;
235+		case TSTATE_CSI:
236+			if (ch >= '0' && ch <= '9') {
237+				t->csi_params[t->csi_param] *= 10;
238+				t->csi_params[t->csi_param] += ch - '0';
239+			}
240+			else if (ch == ';') {
241+				if (t->csi_param + 1 < CSI_PARAMS_MAX)
242+					t->csi_param++;
243+			}
244+			else if (ch == '?' || ch == '>' || ch == '=') {
245+				/* private CSI marker */
246+			}
247+			else {
248+				csi_dispatch(t, c, ch);
249+				t->state = TSTATE_NORMAL;
250+			}
251+			break;
252+		}
253 	}
254 }
255