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 }