st-scrollback-reflow-20230607-211964d.diff - sites - public wiki contents of su… | |
git clone git://git.suckless.org/sites | |
Log | |
Files | |
Refs | |
--- | |
st-scrollback-reflow-20230607-211964d.diff (38252B) | |
--- | |
1 From 633fe6b0231a0f08e6c1e32ae167117a7809cb7f Mon Sep 17 00:00:00 2001 | |
2 From: sewn <[email protected]> | |
3 Date: Wed, 7 Jun 2023 12:27:38 +0300 | |
4 Subject: [PATCH] reflow columns and rows in case of hidden by resize. | |
5 | |
6 Extracted from https://github.com/ashish-yadav11/st | |
7 Based on the works of BeyondMagic et al. | |
8 --- | |
9 st.c | 941 ++++++++++++++++++++++++++++++++++++++++------------------- | |
10 st.h | 25 +- | |
11 2 files changed, 658 insertions(+), 308 deletions(-) | |
12 | |
13 diff --git a/st.c b/st.c | |
14 index afb862b..46ba498 100644 | |
15 --- a/st.c | |
16 +++ b/st.c | |
17 @@ -36,6 +36,7 @@ | |
18 #define STR_BUF_SIZ ESC_BUF_SIZ | |
19 #define STR_ARG_SIZ ESC_ARG_SIZ | |
20 #define HISTSIZE 2000 | |
21 +#define RESIZEBUFFER 1000 | |
22 | |
23 /* macros */ | |
24 #define IS_SET(flag) ((term.mode & (flag)) != 0) | |
25 @@ -43,9 +44,22 @@ | |
26 #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) | |
27 #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) | |
28 #define ISDELIM(u) (u && wcschr(worddelimiters, u)) | |
29 -#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term… | |
30 - term.scr + HISTSIZE + 1) % HISTSIZE] : \ | |
31 - term.line[(y) - term.scr]) | |
32 + | |
33 +#define TLINE(y) ( \ | |
34 + (y) < term.scr ? term.hist[(term.histi + (y) - term.scr + 1 + H… | |
35 + : term.line[(y) - term.scr] \ | |
36 +) | |
37 + | |
38 +#define TLINEABS(y) ( \ | |
39 + (y) < 0 ? term.hist[(term.histi + (y) + 1 + HISTSIZE) % HISTSIZ… | |
40 +) | |
41 + | |
42 +#define UPDATEWRAPNEXT(alt, col) do { \ | |
43 + if ((term.c.state & CURSOR_WRAPNEXT) && term.c.x + term.wrapcwi… | |
44 + term.c.x += term.wrapcwidth[alt]; \ | |
45 + term.c.state &= ~CURSOR_WRAPNEXT; \ | |
46 + } \ | |
47 +} while (0); | |
48 | |
49 enum term_mode { | |
50 MODE_WRAP = 1 << 0, | |
51 @@ -57,6 +71,12 @@ enum term_mode { | |
52 MODE_UTF8 = 1 << 6, | |
53 }; | |
54 | |
55 +enum scroll_mode { | |
56 + SCROLL_RESIZE = -1, | |
57 + SCROLL_NOSAVEHIST = 0, | |
58 + SCROLL_SAVEHIST = 1 | |
59 +}; | |
60 + | |
61 enum cursor_movement { | |
62 CURSOR_SAVE, | |
63 CURSOR_LOAD | |
64 @@ -118,10 +138,11 @@ typedef struct { | |
65 int row; /* nb row */ | |
66 int col; /* nb col */ | |
67 Line *line; /* screen */ | |
68 - Line *alt; /* alternate screen */ | |
69 Line hist[HISTSIZE]; /* history buffer */ | |
70 - int histi; /* history index */ | |
71 - int scr; /* scroll back */ | |
72 + int histi; /* history index */ | |
73 + int histf; /* nb history available */ | |
74 + int scr; /* scroll back */ | |
75 + int wrapcwidth[2]; /* used in updating WRAPNEXT when resizing… | |
76 int *dirty; /* dirtyness of lines */ | |
77 TCursor c; /* cursor */ | |
78 int ocx; /* old cursor col */ | |
79 @@ -179,26 +200,37 @@ static void tprinter(char *, size_t); | |
80 static void tdumpsel(void); | |
81 static void tdumpline(int); | |
82 static void tdump(void); | |
83 -static void tclearregion(int, int, int, int); | |
84 +static void tclearregion(int, int, int, int, int); | |
85 static void tcursor(int); | |
86 +static void tclearglyph(Glyph *, int); | |
87 +static void tresetcursor(void); | |
88 static void tdeletechar(int); | |
89 static void tdeleteline(int); | |
90 static void tinsertblank(int); | |
91 static void tinsertblankline(int); | |
92 -static int tlinelen(int); | |
93 +static int tlinelen(Line len); | |
94 +static int tiswrapped(Line line); | |
95 +static char *tgetglyphs(char *, const Glyph *, const Glyph *); | |
96 +static size_t tgetline(char *, const Glyph *); | |
97 static void tmoveto(int, int); | |
98 static void tmoveato(int, int); | |
99 static void tnewline(int); | |
100 static void tputtab(int); | |
101 static void tputc(Rune); | |
102 static void treset(void); | |
103 -static void tscrollup(int, int, int); | |
104 -static void tscrolldown(int, int, int); | |
105 +static void tscrollup(int, int, int, int); | |
106 +static void tscrolldown(int, int); | |
107 +static void treflow(int, int); | |
108 +static void rscrolldown(int); | |
109 +static void tresizedef(int, int); | |
110 +static void tresizealt(int, int); | |
111 static void tsetattr(const int *, int); | |
112 static void tsetchar(Rune, const Glyph *, int, int); | |
113 static void tsetdirt(int, int); | |
114 static void tsetscroll(int, int); | |
115 static void tswapscreen(void); | |
116 +static void tloaddefscreen(int, int); | |
117 +static void tloadaltscreen(int, int); | |
118 static void tsetmode(int, int, const int *, int); | |
119 static int twrite(const char *, int, int); | |
120 static void tfulldirt(void); | |
121 @@ -212,7 +244,10 @@ static void tstrsequence(uchar); | |
122 static void drawregion(int, int, int, int); | |
123 | |
124 static void selnormalize(void); | |
125 -static void selscroll(int, int); | |
126 +static void selscroll(int, int, int); | |
127 +static void selmove(int); | |
128 +static void selremove(void); | |
129 +static int regionselected(int, int, int, int); | |
130 static void selsnap(int *, int *, int); | |
131 | |
132 static size_t utf8decode(const char *, Rune *, size_t); | |
133 @@ -412,17 +447,46 @@ selinit(void) | |
134 } | |
135 | |
136 int | |
137 -tlinelen(int y) | |
138 +tlinelen(Line line) | |
139 { | |
140 - int i = term.col; | |
141 + int i = term.col - 1; | |
142 + | |
143 + for (; i >= 0 && !(line[i].mode & (ATTR_SET | ATTR_WRAP)); i--); | |
144 + return i + 1; | |
145 +} | |
146 | |
147 - if (TLINE(y)[i - 1].mode & ATTR_WRAP) | |
148 - return i; | |
149 +int | |
150 +tiswrapped(Line line) | |
151 +{ | |
152 + int len = tlinelen(line); | |
153 | |
154 - while (i > 0 && TLINE(y)[i - 1].u == ' ') | |
155 - --i; | |
156 + return len > 0 && (line[len - 1].mode & ATTR_WRAP); | |
157 +} | |
158 | |
159 - return i; | |
160 +char * | |
161 +tgetglyphs(char *buf, const Glyph *gp, const Glyph *lgp) | |
162 +{ | |
163 + while (gp <= lgp) | |
164 + if (gp->mode & ATTR_WDUMMY) { | |
165 + gp++; | |
166 + } else { | |
167 + buf += utf8encode((gp++)->u, buf); | |
168 + } | |
169 + return buf; | |
170 +} | |
171 + | |
172 +size_t | |
173 +tgetline(char *buf, const Glyph *fgp) | |
174 +{ | |
175 + char *ptr; | |
176 + const Glyph *lgp = &fgp[term.col - 1]; | |
177 + | |
178 + while (lgp > fgp && !(lgp->mode & (ATTR_SET | ATTR_WRAP))) | |
179 + lgp--; | |
180 + ptr = tgetglyphs(buf, fgp, lgp); | |
181 + if (!(lgp->mode & ATTR_WRAP)) | |
182 + *(ptr++) = '\n'; | |
183 + return ptr - buf; | |
184 } | |
185 | |
186 void | |
187 @@ -462,10 +526,11 @@ selextend(int col, int row, int type, int done) | |
188 | |
189 sel.oe.x = col; | |
190 sel.oe.y = row; | |
191 - selnormalize(); | |
192 sel.type = type; | |
193 + selnormalize(); | |
194 | |
195 - if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.ty… | |
196 + if (oldey != sel.oe.y || oldex != sel.oe.x || | |
197 + oldtype != sel.type || sel.mode == SEL_EMPTY) | |
198 tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); | |
199 | |
200 sel.mode = done ? SEL_IDLE : SEL_READY; | |
201 @@ -492,36 +557,43 @@ selnormalize(void) | |
202 /* expand selection over line breaks */ | |
203 if (sel.type == SEL_RECTANGULAR) | |
204 return; | |
205 - i = tlinelen(sel.nb.y); | |
206 - if (i < sel.nb.x) | |
207 + | |
208 + i = tlinelen(TLINE(sel.nb.y)); | |
209 + if (sel.nb.x > i) | |
210 sel.nb.x = i; | |
211 - if (tlinelen(sel.ne.y) <= sel.ne.x) | |
212 - sel.ne.x = term.col - 1; | |
213 + if (sel.ne.x >= tlinelen(TLINE(sel.ne.y))) | |
214 + sel.ne.x = term.col - 1; | |
215 } | |
216 | |
217 int | |
218 -selected(int x, int y) | |
219 +regionselected(int x1, int y1, int x2, int y2) | |
220 { | |
221 - if (sel.mode == SEL_EMPTY || sel.ob.x == -1 || | |
222 - sel.alt != IS_SET(MODE_ALTSCREEN)) | |
223 + if (sel.ob.x == -1 || sel.mode == SEL_EMPTY || | |
224 + sel.alt != IS_SET(MODE_ALTSCREEN) || sel.nb.y > y2 || sel.n… | |
225 return 0; | |
226 | |
227 - if (sel.type == SEL_RECTANGULAR) | |
228 - return BETWEEN(y, sel.nb.y, sel.ne.y) | |
229 - && BETWEEN(x, sel.nb.x, sel.ne.x); | |
230 + return (sel.type == SEL_RECTANGULAR) ? sel.nb.x <= x2 && sel.ne… | |
231 + : (sel.nb.y != y2 || sel.nb.x <= x2) && | |
232 + (sel.ne.y != y1 || sel.ne.x >= x1); | |
233 +} | |
234 | |
235 - return BETWEEN(y, sel.nb.y, sel.ne.y) | |
236 - && (y != sel.nb.y || x >= sel.nb.x) | |
237 - && (y != sel.ne.y || x <= sel.ne.x); | |
238 +int | |
239 +selected(int x, int y) | |
240 +{ | |
241 + return regionselected(x, y, x, y); | |
242 } | |
243 | |
244 void | |
245 selsnap(int *x, int *y, int direction) | |
246 { | |
247 int newx, newy, xt, yt; | |
248 + int rtop = 0, rbot = term.row - 1; | |
249 int delim, prevdelim; | |
250 const Glyph *gp, *prevgp; | |
251 | |
252 + if (!IS_SET(MODE_ALTSCREEN)) | |
253 + rtop += -term.histf + term.scr, rbot += term.scr; | |
254 + | |
255 switch (sel.snap) { | |
256 case SNAP_WORD: | |
257 /* | |
258 @@ -536,7 +608,7 @@ selsnap(int *x, int *y, int direction) | |
259 if (!BETWEEN(newx, 0, term.col - 1)) { | |
260 newy += direction; | |
261 newx = (newx + term.col) % term.col; | |
262 - if (!BETWEEN(newy, 0, term.row - 1)) | |
263 + if (!BETWEEN(newy, rtop, rbot)) | |
264 break; | |
265 | |
266 if (direction > 0) | |
267 @@ -547,13 +619,13 @@ selsnap(int *x, int *y, int direction) | |
268 break; | |
269 } | |
270 | |
271 - if (newx >= tlinelen(newy)) | |
272 + if (newx >= tlinelen(TLINE(newy))) | |
273 break; | |
274 | |
275 gp = &TLINE(newy)[newx]; | |
276 delim = ISDELIM(gp->u); | |
277 - if (!(gp->mode & ATTR_WDUMMY) && (delim != prev… | |
278 - || (delim && gp->u != prevgp->u… | |
279 + if (!(gp->mode & ATTR_WDUMMY) && (delim != prev… | |
280 + (delim && !(gp->u == ' ' && prevgp->u == ' … | |
281 break; | |
282 | |
283 *x = newx; | |
284 @@ -570,18 +642,14 @@ selsnap(int *x, int *y, int direction) | |
285 */ | |
286 *x = (direction < 0) ? 0 : term.col - 1; | |
287 if (direction < 0) { | |
288 - for (; *y > 0; *y += direction) { | |
289 - if (!(TLINE(*y-1)[term.col-1].mode | |
290 - & ATTR_WRAP)) { | |
291 + for (; *y > rtop; *y -= 1) { | |
292 + if (!tiswrapped(TLINE(*y-1))) | |
293 break; | |
294 - } | |
295 } | |
296 } else if (direction > 0) { | |
297 - for (; *y < term.row-1; *y += direction) { | |
298 - if (!(TLINE(*y)[term.col-1].mode | |
299 - & ATTR_WRAP)) { | |
300 + for (; *y < rbot; *y += 1) { | |
301 + if (!tiswrapped(TLINE(*y))) | |
302 break; | |
303 - } | |
304 } | |
305 } | |
306 break; | |
307 @@ -592,40 +660,34 @@ char * | |
308 getsel(void) | |
309 { | |
310 char *str, *ptr; | |
311 - int y, bufsize, lastx, linelen; | |
312 - const Glyph *gp, *last; | |
313 + int y, lastx, linelen; | |
314 + const Glyph *gp, *lgp; | |
315 | |
316 - if (sel.ob.x == -1) | |
317 + if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN)) | |
318 return NULL; | |
319 | |
320 - bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; | |
321 - ptr = str = xmalloc(bufsize); | |
322 + str = xmalloc((term.col + 1) * (sel.ne.y - sel.nb.y + 1) * UTF_… | |
323 + ptr = str; | |
324 | |
325 /* append every set & selected glyph to the selection */ | |
326 for (y = sel.nb.y; y <= sel.ne.y; y++) { | |
327 - if ((linelen = tlinelen(y)) == 0) { | |
328 + Line line = TLINE(y); | |
329 + | |
330 + if ((linelen = tlinelen(line)) == 0) { | |
331 *ptr++ = '\n'; | |
332 continue; | |
333 } | |
334 | |
335 if (sel.type == SEL_RECTANGULAR) { | |
336 - gp = &TLINE(y)[sel.nb.x]; | |
337 + gp = &line[sel.nb.x]; | |
338 lastx = sel.ne.x; | |
339 } else { | |
340 - gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; | |
341 + gp = &line[sel.nb.y == y ? sel.nb.x : 0]; | |
342 lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; | |
343 } | |
344 - last = &TLINE(y)[MIN(lastx, linelen-1)]; | |
345 - while (last >= gp && last->u == ' ') | |
346 - --last; | |
347 - | |
348 - for ( ; gp <= last; ++gp) { | |
349 - if (gp->mode & ATTR_WDUMMY) | |
350 - continue; | |
351 - | |
352 - ptr += utf8encode(gp->u, ptr); | |
353 - } | |
354 + lgp = &line[MIN(lastx, linelen-1)]; | |
355 | |
356 + ptr = tgetglyphs(ptr, gp, lgp); | |
357 /* | |
358 * Copy and pasting of line endings is inconsistent | |
359 * in the inconsistent terminal and GUI world. | |
360 @@ -636,10 +698,10 @@ getsel(void) | |
361 * FIXME: Fix the computer world. | |
362 */ | |
363 if ((y < sel.ne.y || lastx >= linelen) && | |
364 - (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTA… | |
365 + (!(lgp->mode & ATTR_WRAP) || sel.type == SEL_RECTAN… | |
366 *ptr++ = '\n'; | |
367 } | |
368 - *ptr = 0; | |
369 + *ptr = '\0'; | |
370 return str; | |
371 } | |
372 | |
373 @@ -648,9 +710,15 @@ selclear(void) | |
374 { | |
375 if (sel.ob.x == -1) | |
376 return; | |
377 + selremove(); | |
378 + tsetdirt(sel.nb.y, sel.ne.y); | |
379 +} | |
380 + | |
381 +void | |
382 +selremove(void) | |
383 +{ | |
384 sel.mode = SEL_IDLE; | |
385 sel.ob.x = -1; | |
386 - tsetdirt(sel.nb.y, sel.ne.y); | |
387 } | |
388 | |
389 void | |
390 @@ -851,10 +919,8 @@ void | |
391 ttywrite(const char *s, size_t n, int may_echo) | |
392 { | |
393 const char *next; | |
394 - Arg arg = (Arg) { .i = term.scr }; | |
395 - | |
396 - kscrolldown(&arg); | |
397 | |
398 + kscrolldown(&((Arg){ .i = term.scr })); | |
399 if (may_echo && IS_SET(MODE_ECHO)) | |
400 twrite(s, n, 1); | |
401 | |
402 @@ -990,7 +1056,7 @@ tsetdirtattr(int attr) | |
403 for (i = 0; i < term.row-1; i++) { | |
404 for (j = 0; j < term.col-1; j++) { | |
405 if (term.line[i][j].mode & attr) { | |
406 - tsetdirt(i, i); | |
407 + term.dirty[i] = 1; | |
408 break; | |
409 } | |
410 } | |
411 @@ -1000,7 +1066,8 @@ tsetdirtattr(int attr) | |
412 void | |
413 tfulldirt(void) | |
414 { | |
415 - tsetdirt(0, term.row-1); | |
416 + for (int i = 0; i < term.row; i++) | |
417 + term.dirty[i] = 1; | |
418 } | |
419 | |
420 void | |
421 @@ -1017,51 +1084,116 @@ tcursor(int mode) | |
422 } | |
423 } | |
424 | |
425 +void | |
426 +tresetcursor(void) | |
427 +{ | |
428 + term.c = (TCursor){ { .mode = ATTR_NULL, .fg = defaultfg, .bg =… | |
429 + .x = 0, .y = 0, .state = CURSOR_DEFAULT }; | |
430 +} | |
431 + | |
432 void | |
433 treset(void) | |
434 { | |
435 uint i; | |
436 + int x, y; | |
437 | |
438 - term.c = (TCursor){{ | |
439 - .mode = ATTR_NULL, | |
440 - .fg = defaultfg, | |
441 - .bg = defaultbg | |
442 - }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; | |
443 + tresetcursor(); | |
444 | |
445 memset(term.tabs, 0, term.col * sizeof(*term.tabs)); | |
446 for (i = tabspaces; i < term.col; i += tabspaces) | |
447 term.tabs[i] = 1; | |
448 term.top = 0; | |
449 + term.histf = 0; | |
450 + term.scr = 0; | |
451 term.bot = term.row - 1; | |
452 term.mode = MODE_WRAP|MODE_UTF8; | |
453 memset(term.trantbl, CS_USA, sizeof(term.trantbl)); | |
454 term.charset = 0; | |
455 | |
456 + selremove(); | |
457 for (i = 0; i < 2; i++) { | |
458 - tmoveto(0, 0); | |
459 - tcursor(CURSOR_SAVE); | |
460 - tclearregion(0, 0, term.col-1, term.row-1); | |
461 + tcursor(CURSOR_SAVE); /* reset saved cursor */ | |
462 + for (y = 0; y < term.row; y++) | |
463 + for (x = 0; x < term.col; x++) | |
464 + tclearglyph(&term.line[y][x], 0); | |
465 tswapscreen(); | |
466 } | |
467 + tfulldirt(); | |
468 } | |
469 | |
470 void | |
471 tnew(int col, int row) | |
472 { | |
473 - term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultb… | |
474 - tresize(col, row); | |
475 - treset(); | |
476 + int i, j; | |
477 + | |
478 + for (i = 0; i < 2; i++) { | |
479 + term.line = xmalloc(row * sizeof(Line)); | |
480 + for (j = 0; j < row; j++) | |
481 + term.line[j] = xmalloc(col * sizeof(Glyph)); | |
482 + term.col = col, term.row = row; | |
483 + tswapscreen(); | |
484 + } | |
485 + term.dirty = xmalloc(row * sizeof(*term.dirty)); | |
486 + term.tabs = xmalloc(col * sizeof(*term.tabs)); | |
487 + for (i = 0; i < HISTSIZE; i++) | |
488 + term.hist[i] = xmalloc(col * sizeof(Glyph)); | |
489 + treset(); | |
490 } | |
491 | |
492 +/* handle it with care */ | |
493 void | |
494 tswapscreen(void) | |
495 { | |
496 - Line *tmp = term.line; | |
497 + static Line *altline; | |
498 + static int altcol, altrow; | |
499 + Line *tmpline = term.line; | |
500 + int tmpcol = term.col, tmprow = term.row; | |
501 | |
502 - term.line = term.alt; | |
503 - term.alt = tmp; | |
504 + term.line = altline; | |
505 + term.col = altcol, term.row = altrow; | |
506 + altline = tmpline; | |
507 + altcol = tmpcol, altrow = tmprow; | |
508 term.mode ^= MODE_ALTSCREEN; | |
509 - tfulldirt(); | |
510 +} | |
511 + | |
512 +void | |
513 +tloaddefscreen(int clear, int loadcursor) | |
514 +{ | |
515 + int col, row, alt = IS_SET(MODE_ALTSCREEN); | |
516 + | |
517 + if (alt) { | |
518 + if (clear) | |
519 + tclearregion(0, 0, term.col-1, term.row-1, 1); | |
520 + col = term.col, row = term.row; | |
521 + tswapscreen(); | |
522 + } | |
523 + if (loadcursor) | |
524 + tcursor(CURSOR_LOAD); | |
525 + if (alt) | |
526 + tresizedef(col, row); | |
527 +} | |
528 + | |
529 +void | |
530 +tloadaltscreen(int clear, int savecursor) | |
531 +{ | |
532 + int col, row, def = !IS_SET(MODE_ALTSCREEN); | |
533 + | |
534 + if (savecursor) | |
535 + tcursor(CURSOR_SAVE); | |
536 + if (def) { | |
537 + col = term.col, row = term.row; | |
538 + tswapscreen(); | |
539 + term.scr = 0; | |
540 + tresizealt(col, row); | |
541 + } | |
542 + if (clear) | |
543 + tclearregion(0, 0, term.col-1, term.row-1, 1); | |
544 +} | |
545 + | |
546 +int | |
547 +tisaltscreen(void) | |
548 +{ | |
549 + return IS_SET(MODE_ALTSCREEN); | |
550 } | |
551 | |
552 void | |
553 @@ -1069,17 +1201,22 @@ kscrolldown(const Arg* a) | |
554 { | |
555 int n = a->i; | |
556 | |
557 - if (n < 0) | |
558 - n = term.row + n; | |
559 + if (!term.scr || IS_SET(MODE_ALTSCREEN)) | |
560 + return; | |
561 | |
562 - if (n > term.scr) | |
563 - n = term.scr; | |
564 + if (n < 0) | |
565 + n = MAX(term.row / -n, 1); | |
566 | |
567 - if (term.scr > 0) { | |
568 + if (n <= term.scr) { | |
569 term.scr -= n; | |
570 - selscroll(0, -n); | |
571 - tfulldirt(); | |
572 + } else { | |
573 + n = term.scr; | |
574 + term.scr = 0; | |
575 } | |
576 + | |
577 + if (sel.ob.x != -1 && !sel.alt) | |
578 + selmove(-n); /* negate change in term.scr */ | |
579 + tfulldirt(); | |
580 } | |
581 | |
582 void | |
583 @@ -1087,92 +1224,118 @@ kscrollup(const Arg* a) | |
584 { | |
585 int n = a->i; | |
586 | |
587 + if (!term.histf || IS_SET(MODE_ALTSCREEN)) | |
588 + return; | |
589 + | |
590 if (n < 0) | |
591 - n = term.row + n; | |
592 + n = MAX(term.row / -n, 1); | |
593 | |
594 - if (term.scr <= HISTSIZE-n) { | |
595 + if (term.scr + n <= term.histf) { | |
596 term.scr += n; | |
597 - selscroll(0, n); | |
598 - tfulldirt(); | |
599 + } else { | |
600 + n = term.histf - term.scr; | |
601 + term.scr = term.histf; | |
602 } | |
603 + | |
604 + if (sel.ob.x != -1 && !sel.alt) | |
605 + selmove(n); /* negate change in term.scr */ | |
606 + tfulldirt(); | |
607 } | |
608 | |
609 void | |
610 -tscrolldown(int orig, int n, int copyhist) | |
611 +tscrolldown(int top, int n) | |
612 { | |
613 - int i; | |
614 + int i, bot = term.bot; | |
615 Line temp; | |
616 | |
617 - LIMIT(n, 0, term.bot-orig+1); | |
618 - if (copyhist) { | |
619 - term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; | |
620 - temp = term.hist[term.histi]; | |
621 - term.hist[term.histi] = term.line[term.bot]; | |
622 - term.line[term.bot] = temp; | |
623 - } | |
624 - | |
625 + if (n <= 0) | |
626 + return; | |
627 + n = MIN(n, bot-top+1); | |
628 | |
629 - tsetdirt(orig, term.bot-n); | |
630 - tclearregion(0, term.bot-n+1, term.col-1, term.bot); | |
631 + tsetdirt(top, bot-n); | |
632 + tclearregion(0, bot-n+1, term.col-1, bot, 1); | |
633 | |
634 - for (i = term.bot; i >= orig+n; i--) { | |
635 + for (i = bot; i >= top+n; i--) { | |
636 temp = term.line[i]; | |
637 term.line[i] = term.line[i-n]; | |
638 term.line[i-n] = temp; | |
639 } | |
640 | |
641 - if (term.scr == 0) | |
642 - selscroll(orig, n); | |
643 + if (sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN)) | |
644 + selscroll(top, bot, n); | |
645 } | |
646 | |
647 void | |
648 -tscrollup(int orig, int n, int copyhist) | |
649 +tscrollup(int top, int bot, int n, int mode) | |
650 { | |
651 - int i; | |
652 + int i, j, s; | |
653 + int alt = IS_SET(MODE_ALTSCREEN); | |
654 + int savehist = !alt && top == 0 && mode != SCROLL_NOSAVEHIST; | |
655 Line temp; | |
656 | |
657 - LIMIT(n, 0, term.bot-orig+1); | |
658 - | |
659 - if (copyhist) { | |
660 - term.histi = (term.histi + 1) % HISTSIZE; | |
661 - temp = term.hist[term.histi]; | |
662 - term.hist[term.histi] = term.line[orig]; | |
663 - term.line[orig] = temp; | |
664 + if (n <= 0) | |
665 + return; | |
666 + n = MIN(n, bot-top+1); | |
667 + | |
668 + if (savehist) { | |
669 + for (i = 0; i < n; i++) { | |
670 + term.histi = (term.histi + 1) % HISTSIZE; | |
671 + temp = term.hist[term.histi]; | |
672 + for (j = 0; j < term.col; j++) | |
673 + tclearglyph(&temp[j], 1); | |
674 + term.hist[term.histi] = term.line[i]; | |
675 + term.line[i] = temp; | |
676 + } | |
677 + term.histf = MIN(term.histf + n, HISTSIZE); | |
678 + s = n; | |
679 + if (term.scr) { | |
680 + j = term.scr; | |
681 + term.scr = MIN(j + n, HISTSIZE); | |
682 + s = j + n - term.scr; | |
683 + } | |
684 + if (mode != SCROLL_RESIZE) | |
685 + tfulldirt(); | |
686 + } else { | |
687 + tclearregion(0, top, term.col-1, top+n-1, 1); | |
688 + tsetdirt(top+n, bot); | |
689 } | |
690 | |
691 - if (term.scr > 0 && term.scr < HISTSIZE) | |
692 - term.scr = MIN(term.scr + n, HISTSIZE-1); | |
693 - | |
694 - tclearregion(0, orig, term.col-1, orig+n-1); | |
695 - tsetdirt(orig+n, term.bot); | |
696 - | |
697 - for (i = orig; i <= term.bot-n; i++) { | |
698 + for (i = top; i <= bot-n; i++) { | |
699 temp = term.line[i]; | |
700 term.line[i] = term.line[i+n]; | |
701 term.line[i+n] = temp; | |
702 } | |
703 | |
704 - if (term.scr == 0) | |
705 - selscroll(orig, -n); | |
706 + if (sel.ob.x != -1 && sel.alt == alt) { | |
707 + if (!savehist) { | |
708 + selscroll(top, bot, -n); | |
709 + } else if (s > 0) { | |
710 + selmove(-s); | |
711 + if (-term.scr + sel.nb.y < -term.histf) | |
712 + selremove(); | |
713 + } | |
714 + } | |
715 } | |
716 | |
717 void | |
718 -selscroll(int orig, int n) | |
719 +selmove(int n) | |
720 { | |
721 - if (sel.ob.x == -1) | |
722 - return; | |
723 + sel.ob.y += n, sel.nb.y += n; | |
724 + sel.oe.y += n, sel.ne.y += n; | |
725 +} | |
726 + | |
727 +void | |
728 +selscroll(int top, int bot, int n) | |
729 +{ | |
730 + /* turn absolute coordinates into relative */ | |
731 + top += term.scr, bot += term.scr; | |
732 | |
733 - if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig… | |
734 + if (BETWEEN(sel.nb.y, top, bot) != BETWEEN(sel.ne.y, top, bot))… | |
735 selclear(); | |
736 - } else if (BETWEEN(sel.nb.y, orig, term.bot)) { | |
737 - sel.ob.y += n; | |
738 - sel.oe.y += n; | |
739 - if (sel.ob.y < term.top || sel.ob.y > term.bot || | |
740 - sel.oe.y < term.top || sel.oe.y > term.bot) { | |
741 + } else if (BETWEEN(sel.nb.y, top, bot)) { | |
742 + selmove(n); | |
743 + if (sel.nb.y < top || sel.ne.y > bot) | |
744 selclear(); | |
745 - } else { | |
746 - selnormalize(); | |
747 - } | |
748 } | |
749 } | |
750 | |
751 @@ -1182,7 +1345,7 @@ tnewline(int first_col) | |
752 int y = term.c.y; | |
753 | |
754 if (y == term.bot) { | |
755 - tscrollup(term.top, 1, 1); | |
756 + tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIST); | |
757 } else { | |
758 y++; | |
759 } | |
760 @@ -1272,89 +1435,93 @@ tsetchar(Rune u, const Glyph *attr, int x, int y) | |
761 } else if (term.line[y][x].mode & ATTR_WDUMMY) { | |
762 term.line[y][x-1].u = ' '; | |
763 term.line[y][x-1].mode &= ~ATTR_WIDE; | |
764 - } | |
765 + } | |
766 | |
767 term.dirty[y] = 1; | |
768 term.line[y][x] = *attr; | |
769 term.line[y][x].u = u; | |
770 + term.line[y][x].mode |= ATTR_SET; | |
771 } | |
772 | |
773 void | |
774 -tclearregion(int x1, int y1, int x2, int y2) | |
775 +tclearglyph(Glyph *gp, int usecurattr) | |
776 { | |
777 - int x, y, temp; | |
778 - Glyph *gp; | |
779 + if (usecurattr) { | |
780 + gp->fg = term.c.attr.fg; | |
781 + gp->bg = term.c.attr.bg; | |
782 + } else { | |
783 + gp->fg = defaultfg; | |
784 + gp->bg = defaultbg; | |
785 + } | |
786 + gp->mode = ATTR_NULL; | |
787 + gp->u = ' '; | |
788 +} | |
789 | |
790 - if (x1 > x2) | |
791 - temp = x1, x1 = x2, x2 = temp; | |
792 - if (y1 > y2) | |
793 - temp = y1, y1 = y2, y2 = temp; | |
794 +void | |
795 +tclearregion(int x1, int y1, int x2, int y2, int usecurattr) | |
796 +{ | |
797 + int x, y; | |
798 | |
799 - LIMIT(x1, 0, term.col-1); | |
800 - LIMIT(x2, 0, term.col-1); | |
801 - LIMIT(y1, 0, term.row-1); | |
802 - LIMIT(y2, 0, term.row-1); | |
803 + /* regionselected() takes relative coordinates */ | |
804 + if (regionselected(x1+term.scr, y1+term.scr, x2+term.scr, y2+te… | |
805 + selremove(); | |
806 | |
807 for (y = y1; y <= y2; y++) { | |
808 term.dirty[y] = 1; | |
809 - for (x = x1; x <= x2; x++) { | |
810 - gp = &term.line[y][x]; | |
811 - if (selected(x, y)) | |
812 - selclear(); | |
813 - gp->fg = term.c.attr.fg; | |
814 - gp->bg = term.c.attr.bg; | |
815 - gp->mode = 0; | |
816 - gp->u = ' '; | |
817 - } | |
818 + for (x = x1; x <= x2; x++) | |
819 + tclearglyph(&term.line[y][x], usecurattr); | |
820 } | |
821 } | |
822 | |
823 void | |
824 tdeletechar(int n) | |
825 { | |
826 - int dst, src, size; | |
827 - Glyph *line; | |
828 - | |
829 - LIMIT(n, 0, term.col - term.c.x); | |
830 + int src, dst, size; | |
831 + Line line; | |
832 | |
833 + if (n <= 0) | |
834 + return; | |
835 dst = term.c.x; | |
836 - src = term.c.x + n; | |
837 + src = MIN(term.c.x + n, term.col); | |
838 size = term.col - src; | |
839 - line = term.line[term.c.y]; | |
840 - | |
841 - memmove(&line[dst], &line[src], size * sizeof(Glyph)); | |
842 - tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); | |
843 + if (size > 0) { /* otherwise src would point beyond the array | |
844 + https://stackoverflow.com/questions/29844298… | |
845 + line = term.line[term.c.y]; | |
846 + memmove(&line[dst], &line[src], size * sizeof(Glyph)); | |
847 + } | |
848 + tclearregion(dst + size, term.c.y, term.col - 1, term.c.y, 1); | |
849 } | |
850 | |
851 void | |
852 tinsertblank(int n) | |
853 { | |
854 - int dst, src, size; | |
855 - Glyph *line; | |
856 + int src, dst, size; | |
857 + Line line; | |
858 | |
859 - LIMIT(n, 0, term.col - term.c.x); | |
860 - | |
861 - dst = term.c.x + n; | |
862 + if (n <= 0) | |
863 + return; | |
864 + dst = MIN(term.c.x + n, term.col); | |
865 src = term.c.x; | |
866 size = term.col - dst; | |
867 - line = term.line[term.c.y]; | |
868 - | |
869 - memmove(&line[dst], &line[src], size * sizeof(Glyph)); | |
870 - tclearregion(src, term.c.y, dst - 1, term.c.y); | |
871 + if (size > 0) { /* otherwise dst would point beyond the array */ | |
872 + line = term.line[term.c.y]; | |
873 + memmove(&line[dst], &line[src], size * sizeof(Glyph)); | |
874 + } | |
875 + tclearregion(src, term.c.y, dst - 1, term.c.y, 1); | |
876 } | |
877 | |
878 void | |
879 tinsertblankline(int n) | |
880 { | |
881 if (BETWEEN(term.c.y, term.top, term.bot)) | |
882 - tscrolldown(term.c.y, n, 0); | |
883 + tscrolldown(term.c.y, n); | |
884 } | |
885 | |
886 void | |
887 tdeleteline(int n) | |
888 { | |
889 if (BETWEEN(term.c.y, term.top, term.bot)) | |
890 - tscrollup(term.c.y, n, 0); | |
891 + tscrollup(term.c.y, term.bot, n, SCROLL_NOSAVEHIST); | |
892 } | |
893 | |
894 int32_t | |
895 @@ -1528,7 +1695,7 @@ tsetscroll(int t, int b) | |
896 void | |
897 tsetmode(int priv, int set, const int *args, int narg) | |
898 { | |
899 - int alt; const int *lim; | |
900 + const int *lim; | |
901 | |
902 for (lim = args + narg; args < lim; ++args) { | |
903 if (priv) { | |
904 @@ -1589,25 +1756,18 @@ tsetmode(int priv, int set, const int *args, int… | |
905 xsetmode(set, MODE_8BIT); | |
906 break; | |
907 case 1049: /* swap screen & set/restore cursor … | |
908 - if (!allowaltscreen) | |
909 - break; | |
910 - tcursor((set) ? CURSOR_SAVE : CURSOR_LO… | |
911 - /* FALLTHROUGH */ | |
912 case 47: /* swap screen */ | |
913 - case 1047: | |
914 + case 1047: /* swap screen, clearing alternate s… | |
915 if (!allowaltscreen) | |
916 break; | |
917 - alt = IS_SET(MODE_ALTSCREEN); | |
918 - if (alt) { | |
919 - tclearregion(0, 0, term.col-1, | |
920 - term.row-1); | |
921 - } | |
922 - if (set ^ alt) /* set is always 1 or 0 … | |
923 - tswapscreen(); | |
924 - if (*args != 1049) | |
925 - break; | |
926 - /* FALLTHROUGH */ | |
927 + if (set) | |
928 + tloadaltscreen(*args == 1049, *… | |
929 + else | |
930 + tloaddefscreen(*args == 1047, *… | |
931 + break; | |
932 case 1048: | |
933 + if (!allowaltscreen) | |
934 + break; | |
935 tcursor((set) ? CURSOR_SAVE : CURSOR_LO… | |
936 break; | |
937 case 2004: /* 2004: bracketed paste mode */ | |
938 @@ -1659,7 +1819,7 @@ void | |
939 csihandle(void) | |
940 { | |
941 char buf[40]; | |
942 - int len; | |
943 + int n, x; | |
944 | |
945 switch (csiescseq.mode[0]) { | |
946 default: | |
947 @@ -1757,20 +1917,30 @@ csihandle(void) | |
948 case 'J': /* ED -- Clear screen */ | |
949 switch (csiescseq.arg[0]) { | |
950 case 0: /* below */ | |
951 - tclearregion(term.c.x, term.c.y, term.col-1, te… | |
952 + tclearregion(term.c.x, term.c.y, term.col-1, te… | |
953 if (term.c.y < term.row-1) { | |
954 - tclearregion(0, term.c.y+1, term.col-1, | |
955 - term.row-1); | |
956 + tclearregion(0, term.c.y+1, term.col-1,… | |
957 } | |
958 break; | |
959 case 1: /* above */ | |
960 - if (term.c.y > 1) | |
961 - tclearregion(0, 0, term.col-1, term.c.y… | |
962 - tclearregion(0, term.c.y, term.c.x, term.c.y); | |
963 + if (term.c.y >= 1) | |
964 + tclearregion(0, 0, term.col-1, term.c.y… | |
965 + tclearregion(0, term.c.y, term.c.x, term.c.y, 1… | |
966 break; | |
967 case 2: /* all */ | |
968 - tclearregion(0, 0, term.col-1, term.row-1); | |
969 - break; | |
970 + if (IS_SET(MODE_ALTSCREEN)) { | |
971 + tclearregion(0, 0, term.col-1, term.row-1, 1); | |
972 + break; | |
973 + } | |
974 + /* vte does this: | |
975 + tscrollup(0, term.row-1, term.row, SCROLL_SAVEH… | |
976 + | |
977 + /* alacritty does this: */ | |
978 + for (n = term.row-1; n >= 0 && tlinelen(term.li… | |
979 + if (n >= 0) | |
980 + tscrollup(0, term.row-1, n+1, SCROLL_SA… | |
981 + tscrollup(0, term.row-1, term.row-n-1, SCROLL_N… | |
982 + break; | |
983 default: | |
984 goto unknown; | |
985 } | |
986 @@ -1778,24 +1948,24 @@ csihandle(void) | |
987 case 'K': /* EL -- Clear line */ | |
988 switch (csiescseq.arg[0]) { | |
989 case 0: /* right */ | |
990 - tclearregion(term.c.x, term.c.y, term.col-1, | |
991 - term.c.y); | |
992 + tclearregion(term.c.x, term.c.y, term.col-1, te… | |
993 break; | |
994 case 1: /* left */ | |
995 - tclearregion(0, term.c.y, term.c.x, term.c.y); | |
996 + tclearregion(0, term.c.y, term.c.x, term.c.y, 1… | |
997 break; | |
998 case 2: /* all */ | |
999 - tclearregion(0, term.c.y, term.col-1, term.c.y); | |
1000 + tclearregion(0, term.c.y, term.col-1, term.c.y,… | |
1001 break; | |
1002 } | |
1003 break; | |
1004 case 'S': /* SU -- Scroll <n> line up */ | |
1005 DEFAULT(csiescseq.arg[0], 1); | |
1006 - tscrollup(term.top, csiescseq.arg[0], 0); | |
1007 + /* xterm, urxvt, alacritty save this in history */ | |
1008 + tscrollup(term.top, term.bot, csiescseq.arg[0], SCROLL_… | |
1009 break; | |
1010 case 'T': /* SD -- Scroll <n> line down */ | |
1011 DEFAULT(csiescseq.arg[0], 1); | |
1012 - tscrolldown(term.top, csiescseq.arg[0], 0); | |
1013 + tscrolldown(term.top, csiescseq.arg[0]); | |
1014 break; | |
1015 case 'L': /* IL -- Insert <n> blank lines */ | |
1016 DEFAULT(csiescseq.arg[0], 1); | |
1017 @@ -1809,9 +1979,11 @@ csihandle(void) | |
1018 tdeleteline(csiescseq.arg[0]); | |
1019 break; | |
1020 case 'X': /* ECH -- Erase <n> char */ | |
1021 + if (csiescseq.arg[0] < 0) | |
1022 + return; | |
1023 DEFAULT(csiescseq.arg[0], 1); | |
1024 - tclearregion(term.c.x, term.c.y, | |
1025 - term.c.x + csiescseq.arg[0] - 1, term.c… | |
1026 + x = MIN(term.c.x + csiescseq.arg[0], term.col) - 1; | |
1027 + tclearregion(term.c.x, term.c.y, x, term.c.y, 1); | |
1028 break; | |
1029 case 'P': /* DCH -- Delete <n> char */ | |
1030 DEFAULT(csiescseq.arg[0], 1); | |
1031 @@ -1837,9 +2009,9 @@ csihandle(void) | |
1032 ttywrite("\033[0n", sizeof("\033[0n") - 1, 0); | |
1033 break; | |
1034 case 6: /* Report Cursor Position (CPR) "<row>;<column>… | |
1035 - len = snprintf(buf, sizeof(buf), "\033[%i;%iR", | |
1036 + n = snprintf(buf, sizeof(buf), "\033[%i;%iR", | |
1037 term.c.y+1, term.c.x+1); | |
1038 - ttywrite(buf, len, 0); | |
1039 + ttywrite(buf, n, 0); | |
1040 break; | |
1041 default: | |
1042 goto unknown; | |
1043 @@ -2137,16 +2309,8 @@ tdumpsel(void) | |
1044 void | |
1045 tdumpline(int n) | |
1046 { | |
1047 - char buf[UTF_SIZ]; | |
1048 - const Glyph *bp, *end; | |
1049 - | |
1050 - bp = &term.line[n][0]; | |
1051 - end = &bp[MIN(tlinelen(n), term.col) - 1]; | |
1052 - if (bp != end || bp->u != ' ') { | |
1053 - for ( ; bp <= end; ++bp) | |
1054 - tprinter(buf, utf8encode(bp->u, buf)); | |
1055 - } | |
1056 - tprinter("\n", 1); | |
1057 + char str[(term.col + 1) * UTF_SIZ]; | |
1058 + tprinter(str, tgetline(str, &term.line[n][0])); | |
1059 } | |
1060 | |
1061 void | |
1062 @@ -2367,7 +2531,7 @@ eschandle(uchar ascii) | |
1063 return 0; | |
1064 case 'D': /* IND -- Linefeed */ | |
1065 if (term.c.y == term.bot) { | |
1066 - tscrollup(term.top, 1, 1); | |
1067 + tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIS… | |
1068 } else { | |
1069 tmoveto(term.c.x, term.c.y+1); | |
1070 } | |
1071 @@ -2380,7 +2544,7 @@ eschandle(uchar ascii) | |
1072 break; | |
1073 case 'M': /* RI -- Reverse index */ | |
1074 if (term.c.y == term.top) { | |
1075 - tscrolldown(term.top, 1, 1); | |
1076 + tscrolldown(term.top, 1); | |
1077 } else { | |
1078 tmoveto(term.c.x, term.c.y-1); | |
1079 } | |
1080 @@ -2523,7 +2687,8 @@ check_control_code: | |
1081 */ | |
1082 return; | |
1083 } | |
1084 - if (selected(term.c.x, term.c.y)) | |
1085 + /* selected() takes relative coordinates */ | |
1086 + if (selected(term.c.x + term.scr, term.c.y + term.scr)) | |
1087 selclear(); | |
1088 | |
1089 gp = &term.line[term.c.y][term.c.x]; | |
1090 @@ -2558,6 +2723,7 @@ check_control_code: | |
1091 if (term.c.x+width < term.col) { | |
1092 tmoveto(term.c.x+width, term.c.y); | |
1093 } else { | |
1094 + term.wrapcwidth[IS_SET(MODE_ALTSCREEN)] = width; | |
1095 term.c.state |= CURSOR_WRAPNEXT; | |
1096 } | |
1097 } | |
1098 @@ -2595,93 +2761,275 @@ twrite(const char *buf, int buflen, int show_ct… | |
1099 } | |
1100 | |
1101 void | |
1102 -tresize(int col, int row) | |
1103 +treflow(int col, int row) | |
1104 { | |
1105 int i, j; | |
1106 - int minrow = MIN(row, term.row); | |
1107 - int mincol = MIN(col, term.col); | |
1108 - int *bp; | |
1109 - TCursor c; | |
1110 - | |
1111 - if (col < 1 || row < 1) { | |
1112 - fprintf(stderr, | |
1113 - "tresize: error resizing to %dx%d\n", col, row); | |
1114 - return; | |
1115 + int oce, nce, bot, scr; | |
1116 + int ox = 0, oy = -term.histf, nx = 0, ny = -1, len; | |
1117 + int cy = -1; /* proxy for new y coordinate of cursor */ | |
1118 + int nlines; | |
1119 + Line *buf, line; | |
1120 + | |
1121 + /* y coordinate of cursor line end */ | |
1122 + for (oce = term.c.y; oce < term.row - 1 && | |
1123 + tiswrapped(term.line[oce]); oce++); | |
1124 + | |
1125 + nlines = term.histf + oce + 1; | |
1126 + if (col < term.col) { | |
1127 + /* each line can take this many lines after reflow */ | |
1128 + j = (term.col + col - 1) / col; | |
1129 + nlines = j * nlines; | |
1130 + if (nlines > HISTSIZE + RESIZEBUFFER + row) { | |
1131 + nlines = HISTSIZE + RESIZEBUFFER + row; | |
1132 + oy = -(nlines / j - oce - 1); | |
1133 + } | |
1134 } | |
1135 + buf = xmalloc(nlines * sizeof(Line)); | |
1136 + do { | |
1137 + if (!nx) | |
1138 + buf[++ny] = xmalloc(col * sizeof(Glyph)); | |
1139 + if (!ox) { | |
1140 + line = TLINEABS(oy); | |
1141 + len = tlinelen(line); | |
1142 + } | |
1143 + if (oy == term.c.y) { | |
1144 + if (!ox) | |
1145 + len = MAX(len, term.c.x + 1); | |
1146 + /* update cursor */ | |
1147 + if (cy < 0 && term.c.x - ox < col - nx) { | |
1148 + term.c.x = nx + term.c.x - ox, cy = ny; | |
1149 + UPDATEWRAPNEXT(0, col); | |
1150 + } | |
1151 + } | |
1152 + /* get reflowed lines in buf */ | |
1153 + if (col - nx > len - ox) { | |
1154 + memcpy(&buf[ny][nx], &line[ox], (len-ox) * size… | |
1155 + nx += len - ox; | |
1156 + if (len == 0 || !(line[len - 1].mode & ATTR_WRA… | |
1157 + for (j = nx; j < col; j++) | |
1158 + tclearglyph(&buf[ny][j], 0); | |
1159 + nx = 0; | |
1160 + } else if (nx > 0) { | |
1161 + buf[ny][nx - 1].mode &= ~ATTR_WRAP; | |
1162 + } | |
1163 + ox = 0, oy++; | |
1164 + } else if (col - nx == len - ox) { | |
1165 + memcpy(&buf[ny][nx], &line[ox], (col-nx) * size… | |
1166 + ox = 0, oy++, nx = 0; | |
1167 + } else/* if (col - nx < len - ox) */ { | |
1168 + memcpy(&buf[ny][nx], &line[ox], (col-nx) * size… | |
1169 + ox += col - nx; | |
1170 + buf[ny][col - 1].mode |= ATTR_WRAP; | |
1171 + nx = 0; | |
1172 + } | |
1173 + } while (oy <= oce); | |
1174 + if (nx) | |
1175 + for (j = nx; j < col; j++) | |
1176 + tclearglyph(&buf[ny][j], 0); | |
1177 | |
1178 - /* | |
1179 - * slide screen to keep cursor where we expect it - | |
1180 - * tscrollup would work here, but we can optimize to | |
1181 - * memmove because we're freeing the earlier lines | |
1182 - */ | |
1183 - for (i = 0; i <= term.c.y - row; i++) { | |
1184 + /* free extra lines */ | |
1185 + for (i = row; i < term.row; i++) | |
1186 free(term.line[i]); | |
1187 - free(term.alt[i]); | |
1188 + /* resize to new height */ | |
1189 + term.line = xrealloc(term.line, row * sizeof(Line)); | |
1190 + | |
1191 + bot = MIN(ny, row - 1); | |
1192 + scr = MAX(row - term.row, 0); | |
1193 + /* update y coordinate of cursor line end */ | |
1194 + nce = MIN(oce + scr, bot); | |
1195 + /* update cursor y coordinate */ | |
1196 + term.c.y = nce - (ny - cy); | |
1197 + if (term.c.y < 0) { | |
1198 + j = nce, nce = MIN(nce + -term.c.y, bot); | |
1199 + term.c.y += nce - j; | |
1200 + while (term.c.y < 0) { | |
1201 + free(buf[ny--]); | |
1202 + term.c.y++; | |
1203 + } | |
1204 } | |
1205 - /* ensure that both src and dst are not NULL */ | |
1206 - if (i > 0) { | |
1207 - memmove(term.line, term.line + i, row * sizeof(Line)); | |
1208 - memmove(term.alt, term.alt + i, row * sizeof(Line)); | |
1209 + /* allocate new rows */ | |
1210 + for (i = row - 1; i > nce; i--) { | |
1211 + term.line[i] = xmalloc(col * sizeof(Glyph)); | |
1212 + for (j = 0; j < col; j++) | |
1213 + tclearglyph(&term.line[i][j], 0); | |
1214 } | |
1215 - for (i += row; i < term.row; i++) { | |
1216 + /* fill visible area */ | |
1217 + for (/*i = nce */; i >= term.row; i--, ny--) | |
1218 + term.line[i] = buf[ny]; | |
1219 + for (/*i = term.row - 1 */; i >= 0; i--, ny--) { | |
1220 free(term.line[i]); | |
1221 - free(term.alt[i]); | |
1222 + term.line[i] = buf[ny]; | |
1223 + } | |
1224 + /* fill lines in history buffer and update term.histf */ | |
1225 + for (/*i = -1 */; ny >= 0 && i >= -HISTSIZE; i--, ny--) { | |
1226 + j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE; | |
1227 + free(term.hist[j]); | |
1228 + term.hist[j] = buf[ny]; | |
1229 } | |
1230 + term.histf = -i - 1; | |
1231 + term.scr = MIN(term.scr, term.histf); | |
1232 + /* resize rest of the history lines */ | |
1233 + for (/*i = -term.histf - 1 */; i >= -HISTSIZE; i--) { | |
1234 + j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE; | |
1235 + term.hist[j] = xrealloc(term.hist[j], col * sizeof(Glyp… | |
1236 + } | |
1237 + free(buf); | |
1238 +} | |
1239 | |
1240 - /* resize to new height */ | |
1241 - term.line = xrealloc(term.line, row * sizeof(Line)); | |
1242 - term.alt = xrealloc(term.alt, row * sizeof(Line)); | |
1243 - term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); | |
1244 - term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); | |
1245 +void | |
1246 +rscrolldown(int n) | |
1247 +{ | |
1248 + int i; | |
1249 + Line temp; | |
1250 | |
1251 - for (i = 0; i < HISTSIZE; i++) { | |
1252 - term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyp… | |
1253 - for (j = mincol; j < col; j++) { | |
1254 - term.hist[i][j] = term.c.attr; | |
1255 - term.hist[i][j].u = ' '; | |
1256 - } | |
1257 - } | |
1258 + /* can never be true as of now | |
1259 + if (IS_SET(MODE_ALTSCREEN)) | |
1260 + return; */ | |
1261 | |
1262 - /* resize each row to new width, zero-pad if needed */ | |
1263 - for (i = 0; i < minrow; i++) { | |
1264 - term.line[i] = xrealloc(term.line[i], col * sizeof(Glyp… | |
1265 - term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyp… | |
1266 - } | |
1267 + if ((n = MIN(n, term.histf)) <= 0) | |
1268 + return; | |
1269 | |
1270 - /* allocate any new rows */ | |
1271 - for (/* i = minrow */; i < row; i++) { | |
1272 - term.line[i] = xmalloc(col * sizeof(Glyph)); | |
1273 - term.alt[i] = xmalloc(col * sizeof(Glyph)); | |
1274 + for (i = term.c.y + n; i >= n; i--) { | |
1275 + temp = term.line[i]; | |
1276 + term.line[i] = term.line[i-n]; | |
1277 + term.line[i-n] = temp; | |
1278 } | |
1279 + for (/*i = n - 1 */; i >= 0; i--) { | |
1280 + temp = term.line[i]; | |
1281 + term.line[i] = term.hist[term.histi]; | |
1282 + term.hist[term.histi] = temp; | |
1283 + term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; | |
1284 + } | |
1285 + term.c.y += n; | |
1286 + term.histf -= n; | |
1287 + if ((i = term.scr - n) >= 0) { | |
1288 + term.scr = i; | |
1289 + } else { | |
1290 + term.scr = 0; | |
1291 + if (sel.ob.x != -1 && !sel.alt) | |
1292 + selmove(-i); | |
1293 + } | |
1294 +} | |
1295 + | |
1296 +void | |
1297 +tresize(int col, int row) | |
1298 +{ | |
1299 + int *bp; | |
1300 + | |
1301 + /* col and row are always MAX(_, 1) | |
1302 + if (col < 1 || row < 1) { | |
1303 + fprintf(stderr, "tresize: error resizing to %dx%d\n", c… | |
1304 + return; | |
1305 + } */ | |
1306 + | |
1307 + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); | |
1308 + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); | |
1309 if (col > term.col) { | |
1310 bp = term.tabs + term.col; | |
1311 - | |
1312 memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); | |
1313 while (--bp > term.tabs && !*bp) | |
1314 /* nothing */ ; | |
1315 for (bp += tabspaces; bp < term.tabs + col; bp += tabsp… | |
1316 *bp = 1; | |
1317 } | |
1318 - /* update terminal size */ | |
1319 - term.col = col; | |
1320 - term.row = row; | |
1321 - /* reset scrolling region */ | |
1322 - tsetscroll(0, row-1); | |
1323 - /* make use of the LIMIT in tmoveto */ | |
1324 - tmoveto(term.c.x, term.c.y); | |
1325 - /* Clearing both screens (it makes dirty all lines) */ | |
1326 - c = term.c; | |
1327 - for (i = 0; i < 2; i++) { | |
1328 - if (mincol < col && 0 < minrow) { | |
1329 - tclearregion(mincol, 0, col - 1, minrow - 1); | |
1330 + | |
1331 + if (IS_SET(MODE_ALTSCREEN)) | |
1332 + tresizealt(col, row); | |
1333 + else | |
1334 + tresizedef(col, row); | |
1335 +} | |
1336 + | |
1337 +void | |
1338 +tresizedef(int col, int row) | |
1339 +{ | |
1340 + int i, j; | |
1341 + | |
1342 + /* return if dimensions haven't changed */ | |
1343 + if (term.col == col && term.row == row) { | |
1344 + tfulldirt(); | |
1345 + return; | |
1346 + } | |
1347 + if (col != term.col) { | |
1348 + if (!sel.alt) | |
1349 + selremove(); | |
1350 + treflow(col, row); | |
1351 + } else { | |
1352 + /* slide screen up if otherwise cursor would get out of… | |
1353 + if (term.c.y >= row) { | |
1354 + tscrollup(0, term.row - 1, term.c.y - row + 1, … | |
1355 + term.c.y = row - 1; | |
1356 } | |
1357 - if (0 < col && minrow < row) { | |
1358 - tclearregion(0, minrow, col - 1, row - 1); | |
1359 + for (i = row; i < term.row; i++) | |
1360 + free(term.line[i]); | |
1361 + | |
1362 + /* resize to new height */ | |
1363 + term.line = xrealloc(term.line, row * sizeof(Line)); | |
1364 + /* allocate any new rows */ | |
1365 + for (i = term.row; i < row; i++) { | |
1366 + term.line[i] = xmalloc(col * sizeof(Glyph)); | |
1367 + for (j = 0; j < col; j++) | |
1368 + tclearglyph(&term.line[i][j], 0); | |
1369 } | |
1370 - tswapscreen(); | |
1371 - tcursor(CURSOR_LOAD); | |
1372 + /* scroll down as much as height has increased */ | |
1373 + rscrolldown(row - term.row); | |
1374 + } | |
1375 + /* update terminal size */ | |
1376 + term.col = col, term.row = row; | |
1377 + /* reset scrolling region */ | |
1378 + term.top = 0, term.bot = row - 1; | |
1379 + /* dirty all lines */ | |
1380 + tfulldirt(); | |
1381 +} | |
1382 + | |
1383 +void | |
1384 +tresizealt(int col, int row) | |
1385 +{ | |
1386 + int i, j; | |
1387 + | |
1388 + /* return if dimensions haven't changed */ | |
1389 + if (term.col == col && term.row == row) { | |
1390 + tfulldirt(); | |
1391 + return; | |
1392 } | |
1393 - term.c = c; | |
1394 + if (sel.alt) | |
1395 + selremove(); | |
1396 + /* slide screen up if otherwise cursor would get out of the scr… | |
1397 + for (i = 0; i <= term.c.y - row; i++) | |
1398 + free(term.line[i]); | |
1399 + if (i > 0) { | |
1400 + /* ensure that both src and dst are not NULL */ | |
1401 + memmove(term.line, term.line + i, row * sizeof(Line)); | |
1402 + term.c.y = row - 1; | |
1403 + } | |
1404 + for (i += row; i < term.row; i++) | |
1405 + free(term.line[i]); | |
1406 + /* resize to new height */ | |
1407 + term.line = xrealloc(term.line, row * sizeof(Line)); | |
1408 + /* resize to new width */ | |
1409 + for (i = 0; i < MIN(row, term.row); i++) { | |
1410 + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyp… | |
1411 + for (j = term.col; j < col; j++) | |
1412 + tclearglyph(&term.line[i][j], 0); | |
1413 + } | |
1414 + /* allocate any new rows */ | |
1415 + for (/*i = MIN(row, term.row) */; i < row; i++) { | |
1416 + term.line[i] = xmalloc(col * sizeof(Glyph)); | |
1417 + for (j = 0; j < col; j++) | |
1418 + tclearglyph(&term.line[i][j], 0); | |
1419 + } | |
1420 + /* update cursor */ | |
1421 + if (term.c.x >= col) { | |
1422 + term.c.state &= ~CURSOR_WRAPNEXT; | |
1423 + term.c.x = col - 1; | |
1424 + } else { | |
1425 + UPDATEWRAPNEXT(1, col); | |
1426 + } | |
1427 + /* update terminal size */ | |
1428 + term.col = col, term.row = row; | |
1429 + /* reset scrolling region */ | |
1430 + term.top = 0, term.bot = row - 1; | |
1431 + /* dirty all lines */ | |
1432 + tfulldirt(); | |
1433 } | |
1434 | |
1435 void | |
1436 @@ -2721,9 +3069,8 @@ draw(void) | |
1437 cx--; | |
1438 | |
1439 drawregion(0, 0, term.col, term.row); | |
1440 - if (term.scr == 0) | |
1441 - xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | |
1442 - term.ocx, term.ocy, term.line[term.ocy]… | |
1443 + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | |
1444 + term.ocx, term.ocy, term.line[term.ocy][term.oc… | |
1445 term.ocx = cx; | |
1446 term.ocy = term.c.y; | |
1447 xfinishdraw(); | |
1448 diff --git a/st.h b/st.h | |
1449 index 818a6f8..514ec08 100644 | |
1450 --- a/st.h | |
1451 +++ b/st.h | |
1452 @@ -22,17 +22,19 @@ | |
1453 | |
1454 enum glyph_attribute { | |
1455 ATTR_NULL = 0, | |
1456 - ATTR_BOLD = 1 << 0, | |
1457 - ATTR_FAINT = 1 << 1, | |
1458 - ATTR_ITALIC = 1 << 2, | |
1459 - ATTR_UNDERLINE = 1 << 3, | |
1460 - ATTR_BLINK = 1 << 4, | |
1461 - ATTR_REVERSE = 1 << 5, | |
1462 - ATTR_INVISIBLE = 1 << 6, | |
1463 - ATTR_STRUCK = 1 << 7, | |
1464 - ATTR_WRAP = 1 << 8, | |
1465 - ATTR_WIDE = 1 << 9, | |
1466 - ATTR_WDUMMY = 1 << 10, | |
1467 + ATTR_SET = 1 << 0, | |
1468 + ATTR_BOLD = 1 << 1, | |
1469 + ATTR_FAINT = 1 << 2, | |
1470 + ATTR_ITALIC = 1 << 3, | |
1471 + ATTR_UNDERLINE = 1 << 4, | |
1472 + ATTR_BLINK = 1 << 5, | |
1473 + ATTR_REVERSE = 1 << 6, | |
1474 + ATTR_INVISIBLE = 1 << 7, | |
1475 + ATTR_STRUCK = 1 << 8, | |
1476 + ATTR_WRAP = 1 << 9, | |
1477 + ATTR_WIDE = 1 << 10, | |
1478 + ATTR_WDUMMY = 1 << 11, | |
1479 + ATTR_SELECTED = 1 << 12, | |
1480 ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, | |
1481 }; | |
1482 | |
1483 @@ -90,6 +92,7 @@ void toggleprinter(const Arg *); | |
1484 | |
1485 int tattrset(int); | |
1486 void tnew(int, int); | |
1487 +int tisaltscreen(void); | |
1488 void tresize(int, int); | |
1489 void tsetdirtattr(int); | |
1490 void ttyhangup(void); | |
1491 -- | |
1492 2.40.1 | |
1493 |