st-scrollback-reflow-20230607-211964d.diff - sites - public wiki contents of suckless.org
(HTM) git clone git://git.suckless.org/sites
(DIR) Log
(DIR) Files
(DIR) Refs
---
st-scrollback-reflow-20230607-211964d.diff (38252B)
---
1 From 633fe6b0231a0f08e6c1e32ae167117a7809cb7f Mon Sep 17 00:00:00 2001
2 From: sewn <sewn@disroot.org>
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.histi - \
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 + HISTSIZE) % HISTSIZE] \
35 + : term.line[(y) - term.scr] \
36 +)
37 +
38 +#define TLINEABS(y) ( \
39 + (y) < 0 ? term.hist[(term.histi + (y) + 1 + HISTSIZE) % HISTSIZE] : term.line[(y)] \
40 +)
41 +
42 +#define UPDATEWRAPNEXT(alt, col) do { \
43 + if ((term.c.state & CURSOR_WRAPNEXT) && term.c.x + term.wrapcwidth[alt] < col) { \
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.type || sel.mode == SEL_EMPTY)
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.ne.y < y1)
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.x >= x1
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 != prevdelim
278 - || (delim && gp->u != prevgp->u)))
279 + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim ||
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_SIZ);
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_RECTANGULAR))
365 + (!(lgp->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))
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 = defaultbg },
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 = defaultbg } } };
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, term.bot)) {
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+term.scr))
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 narg)
905 xsetmode(set, MODE_8BIT);
906 break;
907 case 1049: /* swap screen & set/restore cursor as xterm */
908 - if (!allowaltscreen)
909 - break;
910 - tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
911 - /* FALLTHROUGH */
912 case 47: /* swap screen */
913 - case 1047:
914 + case 1047: /* swap screen, clearing alternate screen */
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, *args == 1049);
929 + else
930 + tloaddefscreen(*args == 1047, *args == 1049);
931 + break;
932 case 1048:
933 + if (!allowaltscreen)
934 + break;
935 tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
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, term.c.y);
952 + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 1);
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, term.row-1, 1);
957 }
958 break;
959 case 1: /* above */
960 - if (term.c.y > 1)
961 - tclearregion(0, 0, term.col-1, term.c.y-1);
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-1, 1);
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_SAVEHIST); */
976 +
977 + /* alacritty does this: */
978 + for (n = term.row-1; n >= 0 && tlinelen(term.line[n]) == 0; n--);
979 + if (n >= 0)
980 + tscrollup(0, term.row-1, n+1, SCROLL_SAVEHIST);
981 + tscrollup(0, term.row-1, term.row-n-1, SCROLL_NOSAVEHIST);
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, term.c.y, 1);
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, 1);
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_SAVEHIST);
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.y);
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>R" */
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_SAVEHIST);
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_ctrl)
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) * sizeof(Glyph));
1155 + nx += len - ox;
1156 + if (len == 0 || !(line[len - 1].mode & ATTR_WRAP)) {
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) * sizeof(Glyph));
1166 + ox = 0, oy++, nx = 0;
1167 + } else/* if (col - nx < len - ox) */ {
1168 + memcpy(&buf[ny][nx], &line[ox], (col-nx) * sizeof(Glyph));
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(Glyph));
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(Glyph));
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(Glyph));
1265 - term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph));
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", col, row);
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 += tabspaces)
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 the screen */
1353 + if (term.c.y >= row) {
1354 + tscrollup(0, term.row - 1, term.c.y - row + 1, SCROLL_RESIZE);
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 screen */
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(Glyph));
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][term.ocx]);
1443 + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
1444 + term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
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