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