commit 500c495

uint  ·  2026-05-14 18:05:51 +0000 UTC
parent cd21e72
add segmented scrolling (for programs like vim)
2 files changed,  +116, -26
+8, -6
 1@@ -32,21 +32,23 @@ typedef struct {
 2 typedef struct {
 3 	Rune*          runes;
 4 	Rune*          alt;
 5+	Caret          saved;
 6 	uint32_t       fg;
 7 	uint32_t       bg;
 8 
 9 	int            cols;
10 	int            rows;
11+	int            scroll_top;
12+	int            scroll_bot;
13 
14 	int            ptyfd;
15 	pid_t          ptypid;
16 
17-	enum TState   state;
18-	int           csi_params[CSI_PARAMS_MAX];
19-	int           csi_idx;
20-	Caret         saved;
21-	char          utf8[4];
22-	size_t        utf8_len;
23+	enum TState    state;
24+	int            csi_params[CSI_PARAMS_MAX];
25+	int            csi_idx;
26+	char           utf8[4];
27+	size_t         utf8_len;
28 } Term;
29 
30 /**
+108, -20
  1@@ -14,6 +14,8 @@ static void csi_reset(Term* t);
  2 static void erase_display(Term* t, Caret* c);
  3 static void erase_line(Term* t, Caret* c);
  4 static void reset_rune(Term* t, int x, int y);
  5+static void scroll_down(Term* t, int top, int bot, int n);
  6+static void scroll_up(Term* t, int top, int bot, int n);
  7 static void sgr(Term* t);
  8 static void utf8_flush(Term* t, Caret* c);
  9 static void utf8_putc(Term* t, Caret* c, unsigned char ch);
 10@@ -95,6 +97,13 @@ static void csi_dispatch(Term* t, Caret* c, unsigned char ch)
 11 		erase_line(t, c);
 12 		break;
 13 
 14+	case 'X': { /* ECH - Erase Character */
 15+		int n = csi_arg(t, 0, 1);
 16+		for (int x = c->x; x < c->x + n && x < t->cols; x++)
 17+			reset_rune(t, x, c->y);
 18+		break;
 19+	}
 20+
 21 	case 'm': /* SGR - Select Graphics Rendition - ESC[nm, colours */
 22 		sgr(t);
 23 		break;
 24@@ -155,6 +164,24 @@ static void csi_dispatch(Term* t, Caret* c, unsigned char ch)
 25 			  break;
 26 	}
 27 
 28+	case 'L': /* IL - Insert Line */
 29+		if (c->y >= t->scroll_top && c->y <= t->scroll_bot)
 30+			scroll_down(t, c->y, t->scroll_bot, csi_arg(t, 0, 1));
 31+		break;
 32+
 33+	case 'M': /* DL - Delete Line */
 34+		if (c->y >= t->scroll_top && c->y <= t->scroll_bot)
 35+			scroll_up(t, c->y, t->scroll_bot, csi_arg(t, 0, 1));
 36+		break;
 37+
 38+	case 'S': /* SU - Scroll Up */
 39+		scroll_up(t, t->scroll_top, t->scroll_bot, csi_arg(t, 0, 1));
 40+		break;
 41+
 42+	case 'T': /* SD - Scroll Down */
 43+		scroll_down(t, t->scroll_top, t->scroll_bot, csi_arg(t, 0, 1));
 44+		break;
 45+
 46 	case 'h': /* set mode */
 47 	case 'l': { /* reset mode */
 48 		bool on = ch == 'h';
 49@@ -176,8 +203,21 @@ static void csi_dispatch(Term* t, Caret* c, unsigned char ch)
 50 		}
 51 		break;
 52 	}
 53-	case 'r': /* scrolling region */
 54+	case 'r': { /* DECSTBM - scrolling region */
 55+		int top = csi_arg(t, 0, 1) - 1;
 56+		int bot = csi_arg(t, 1, t->rows) - 1;
 57+		if (top < 0)
 58+			top = 0;
 59+		if (bot >= t->rows)
 60+			bot = t->rows - 1;
 61+		if (top < bot) {
 62+			t->scroll_top = top;
 63+			t->scroll_bot = bot;
 64+			c->x = 0;
 65+			c->y = 0;
 66+		}
 67 		break;
 68+	}
 69 	case 's': t->saved = *c; break; /* save cursor */
 70 	case 'u': *c = t->saved; break; /* restore cursor */
 71 
 72@@ -237,7 +277,11 @@ static void erase_display(Term* t, Caret* c)
 73  */
 74 static void erase_line(Term* t, Caret* c)
 75 {
 76-	for (int x = c->x; x < t->cols; x++)
 77+	int mode = csi_arg(t, 0, 0);
 78+	int from = mode == 0 ? c->x : 0;
 79+	int to = mode == 1 ? c->x : t->cols - 1;
 80+
 81+	for (int x = from; x <= to; x++)
 82 		reset_rune(t, x, c->y);
 83 }
 84 
 85@@ -252,6 +296,46 @@ static void reset_rune(Term* t, int x, int y)
 86 	r->dmg = true;
 87 }
 88 
 89+/** TODO
 90+ */
 91+static void scroll_down(Term* t, int top, int bot, int n)
 92+{
 93+	int cols = t->cols;
 94+	int rows = bot - top + 1;
 95+
 96+	if (n > rows)
 97+		n = rows;
 98+	memmove(
 99+		&RUNE(t, 0, top + n), &RUNE(t, 0, top),
100+		sizeof(Rune) * cols * (rows - n)
101+	);
102+	for (int y = top; y < top + n; y++) {
103+		for (int x = 0; x < cols; x++)
104+			reset_rune(t, x, y);
105+	}
106+	term_damage_all(t);
107+}
108+
109+/** TODO
110+ */
111+static void scroll_up(Term* t, int top, int bot, int n)
112+{
113+	int cols = t->cols;
114+	int rows = bot - top + 1;
115+
116+	if (n > rows)
117+		n = rows;
118+	memmove(
119+		&RUNE(t, 0, top), &RUNE(t, 0, top + n),
120+		sizeof(Rune) * cols * (rows - n)
121+	);
122+	for (int y = bot - n + 1; y <= bot; y++) {
123+		for (int x = 0; x < cols; x++)
124+			reset_rune(t, x, y);
125+	}
126+	term_damage_all(t);
127+}
128+
129 /** TODO
130  */
131 static void sgr(Term* t)
132@@ -374,11 +458,11 @@ void term_putc(Term* t, Caret* c, uint32_t cp)
133 		break;
134 	case '\n':
135 		c->x = 0;
136-		if (++c->y >= t->rows) {
137+		if (c->y == t->scroll_bot) {
138 			term_scroll(t);
139-			/* move to bottom */
140-			c->y = t->rows - 1;
141 		}
142+		else if (c->y < t->rows - 1)
143+			c->y++;
144 		break;
145 	case '\b':
146 		if (c->x > 0)
147@@ -465,6 +549,8 @@ int term_resize(Term* t, Caret* c, int cols, int rows)
148 	t->runes = nr;
149 	t->cols = cols;
150 	t->rows = rows;
151+	t->scroll_top = 0;
152+	t->scroll_bot = rows - 1;
153 
154 	if (c->x >= cols)
155 		c->x = cols - 1;
156@@ -484,21 +570,7 @@ int term_resize(Term* t, Caret* c, int cols, int rows)
157 
158 void term_scroll(Term* t)
159 {
160-	Rune* runes = t->runes;
161-	int cols = t->cols;
162-	int rows = t->rows;
163-
164-	/* TODO: inefficient */
165-	memmove(runes, runes + cols, sizeof(Rune)*cols * (rows - 1));
166-	term_damage_all(t);
167-
168-	for (int x = 0; x < t->cols; x++) {
169-		Rune* rune = &RUNE(t, x, t->rows - 1);
170-		rune->cp = ' ';
171-		rune->fg = t->fg;
172-		rune->bg = t->bg;
173-		rune->dmg = true;
174-	}
175+	scroll_up(t, t->scroll_top, t->scroll_bot, 1);
176 }
177 
178 bool term_write(Term* t, Caret* c, const char* s, size_t n)
179@@ -533,6 +605,22 @@ bool term_write(Term* t, Caret* c, const char* s, size_t n)
180 				*c = t->saved;
181 				t->state = TSTATE_NORMAL;
182 			}
183+			else if (ch == 'M') {
184+				if (c->y == t->scroll_top)
185+					scroll_down(t, t->scroll_top, t->scroll_bot, 1);
186+				else if (c->y > 0)
187+					c->y--;
188+				t->state = TSTATE_NORMAL;
189+			}
190+			else if (ch == 'D' || ch == 'E') {
191+				if (ch == 'E')
192+					c->x = 0;
193+				if (c->y == t->scroll_bot)
194+					scroll_up(t, t->scroll_top, t->scroll_bot, 1);
195+				else if (c->y < t->rows - 1)
196+					c->y++;
197+				t->state = TSTATE_NORMAL;
198+			}
199 			else {
200 				t->state = TSTATE_NORMAL;
201 			}