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