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