summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <raisersoft@proton.me>2026-02-05 23:51:23 +0100
committersalmonik <raisersoft@proton.me>2026-02-05 23:51:23 +0100
commitae29d17209e8d22dff8a242369668a2f8f540046 (patch)
tree30b33b901d0ac92bbdc6f4d0d505ec8072cc0cda
parentAdd different cursor styles (diff)
downloadst-ae29d17209e8d22dff8a242369668a2f8f540046.tar.gz
st-ae29d17209e8d22dff8a242369668a2f8f540046.zip
scrollback
-rw-r--r--config.def.h2
-rw-r--r--st.c359
-rw-r--r--st.h1
-rw-r--r--x.c2
4 files changed, 244 insertions, 120 deletions
diff --git a/config.def.h b/config.def.h
index 36c10d1..4f0b27e 100644
--- a/config.def.h
+++ b/config.def.h
@@ -208,6 +208,8 @@ static Shortcut shortcuts[] = {
208 { TERMMOD, XK_Y, selpaste, {.i = 0} }, 208 { TERMMOD, XK_Y, selpaste, {.i = 0} },
209 { ShiftMask, XK_Insert, selpaste, {.i = 0} }, 209 { ShiftMask, XK_Insert, selpaste, {.i = 0} },
210 { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, 210 { TERMMOD, XK_Num_Lock, numlock, {.i = 0} },
211 { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} },
212 { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} },
211}; 213};
212 214
213/* 215/*
diff --git a/st.c b/st.c
index 6f40e35..17fc9fb 100644
--- a/st.c
+++ b/st.c
@@ -43,6 +43,10 @@
43#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) 43#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
44#define ISDELIM(u) (u && wcschr(worddelimiters, u)) 44#define ISDELIM(u) (u && wcschr(worddelimiters, u))
45 45
46#define TSCREEN term.screen[IS_SET(MODE_ALTSCREEN)]
47#define TLINEOFFSET(y) (((y) + TSCREEN.cur - TSCREEN.off + TSCREEN.size) % TSCREEN.size)
48#define TLINE(y) (TSCREEN.buffer[TLINEOFFSET(y)])
49
46enum term_mode { 50enum term_mode {
47 MODE_WRAP = 1 << 0, 51 MODE_WRAP = 1 << 0,
48 MODE_INSERT = 1 << 1, 52 MODE_INSERT = 1 << 1,
@@ -109,12 +113,21 @@ typedef struct {
109 int alt; 113 int alt;
110} Selection; 114} Selection;
111 115
116/* Screen lines */
117typedef struct {
118 Line* buffer; /* ring buffer */
119 int size; /* size of buffer */
120 int cur; /* start of active screen */
121 int off; /* scrollback line offset */
122 TCursor sc; /* saved cursor */
123} LineBuffer;
124
112/* Internal representation of the screen */ 125/* Internal representation of the screen */
113typedef struct { 126typedef struct {
114 int row; /* nb row */ 127 int row; /* nb row */
115 int col; /* nb col */ 128 int col; /* nb col */
116 Line *line; /* screen */ 129 LineBuffer screen[2]; /* screen and alternate screen */
117 Line *alt; /* alternate screen */ 130 int linelen; /* allocated line length */
118 int *dirty; /* dirtyness of lines */ 131 int *dirty; /* dirtyness of lines */
119 TCursor c; /* cursor */ 132 TCursor c; /* cursor */
120 int ocx; /* old cursor col */ 133 int ocx; /* old cursor col */
@@ -203,6 +216,8 @@ static void tdeftran(char);
203static void tstrsequence(uchar); 216static void tstrsequence(uchar);
204 217
205static void drawregion(int, int, int, int); 218static void drawregion(int, int, int, int);
219static void clearline(Line, Glyph, int, int);
220static Line ensureline(Line);
206 221
207static void selnormalize(void); 222static void selnormalize(void);
208static void selscroll(int, int); 223static void selscroll(int, int);
@@ -408,11 +423,12 @@ int
408tlinelen(int y) 423tlinelen(int y)
409{ 424{
410 int i = term.col; 425 int i = term.col;
426 Line line = TLINE(y);
411 427
412 if (term.line[y][i - 1].mode & ATTR_WRAP) 428 if (line[i - 1].mode & ATTR_WRAP)
413 return i; 429 return i;
414 430
415 while (i > 0 && term.line[y][i - 1].u == ' ') 431 while (i > 0 && line[i - 1].u == ' ')
416 --i; 432 --i;
417 433
418 return i; 434 return i;
@@ -521,7 +537,7 @@ selsnap(int *x, int *y, int direction)
521 * Snap around if the word wraps around at the end or 537 * Snap around if the word wraps around at the end or
522 * beginning of a line. 538 * beginning of a line.
523 */ 539 */
524 prevgp = &term.line[*y][*x]; 540 prevgp = &TLINE(*y)[*x];
525 prevdelim = ISDELIM(prevgp->u); 541 prevdelim = ISDELIM(prevgp->u);
526 for (;;) { 542 for (;;) {
527 newx = *x + direction; 543 newx = *x + direction;
@@ -536,14 +552,14 @@ selsnap(int *x, int *y, int direction)
536 yt = *y, xt = *x; 552 yt = *y, xt = *x;
537 else 553 else
538 yt = newy, xt = newx; 554 yt = newy, xt = newx;
539 if (!(term.line[yt][xt].mode & ATTR_WRAP)) 555 if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
540 break; 556 break;
541 } 557 }
542 558
543 if (newx >= tlinelen(newy)) 559 if (newx >= tlinelen(newy))
544 break; 560 break;
545 561
546 gp = &term.line[newy][newx]; 562 gp = &TLINE(newy)[newx];
547 delim = ISDELIM(gp->u); 563 delim = ISDELIM(gp->u);
548 if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim 564 if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
549 || (delim && gp->u != prevgp->u))) 565 || (delim && gp->u != prevgp->u)))
@@ -564,14 +580,14 @@ selsnap(int *x, int *y, int direction)
564 *x = (direction < 0) ? 0 : term.col - 1; 580 *x = (direction < 0) ? 0 : term.col - 1;
565 if (direction < 0) { 581 if (direction < 0) {
566 for (; *y > 0; *y += direction) { 582 for (; *y > 0; *y += direction) {
567 if (!(term.line[*y-1][term.col-1].mode 583 if (!(TLINE(*y-1)[term.col-1].mode
568 & ATTR_WRAP)) { 584 & ATTR_WRAP)) {
569 break; 585 break;
570 } 586 }
571 } 587 }
572 } else if (direction > 0) { 588 } else if (direction > 0) {
573 for (; *y < term.row-1; *y += direction) { 589 for (; *y < term.row-1; *y += direction) {
574 if (!(term.line[*y][term.col-1].mode 590 if (!(TLINE(*y)[term.col-1].mode
575 & ATTR_WRAP)) { 591 & ATTR_WRAP)) {
576 break; 592 break;
577 } 593 }
@@ -602,13 +618,13 @@ getsel(void)
602 } 618 }
603 619
604 if (sel.type == SEL_RECTANGULAR) { 620 if (sel.type == SEL_RECTANGULAR) {
605 gp = &term.line[y][sel.nb.x]; 621 gp = &TLINE(y)[sel.nb.x];
606 lastx = sel.ne.x; 622 lastx = sel.ne.x;
607 } else { 623 } else {
608 gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; 624 gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0];
609 lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; 625 lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
610 } 626 }
611 last = &term.line[y][MIN(lastx, linelen-1)]; 627 last = &TLINE(y)[MIN(lastx, linelen-1)];
612 while (last >= gp && last->u == ' ') 628 while (last >= gp && last->u == ' ')
613 --last; 629 --last;
614 630
@@ -949,12 +965,15 @@ int
949tattrset(int attr) 965tattrset(int attr)
950{ 966{
951 int i, j; 967 int i, j;
968 int y = TLINEOFFSET(0);
952 969
953 for (i = 0; i < term.row-1; i++) { 970 for (i = 0; i < term.row-1; i++) {
971 Line line = TSCREEN.buffer[y];
954 for (j = 0; j < term.col-1; j++) { 972 for (j = 0; j < term.col-1; j++) {
955 if (term.line[i][j].mode & attr) 973 if (line[j].mode & attr)
956 return 1; 974 return 1;
957 } 975 }
976 y = (y+1) % TSCREEN.size;
958 } 977 }
959 978
960 return 0; 979 return 0;
@@ -979,14 +998,17 @@ void
979tsetdirtattr(int attr) 998tsetdirtattr(int attr)
980{ 999{
981 int i, j; 1000 int i, j;
1001 int y = TLINEOFFSET(0);
982 1002
983 for (i = 0; i < term.row-1; i++) { 1003 for (i = 0; i < term.row-1; i++) {
1004 Line line = TSCREEN.buffer[y];
984 for (j = 0; j < term.col-1; j++) { 1005 for (j = 0; j < term.col-1; j++) {
985 if (term.line[i][j].mode & attr) { 1006 if (line[j].mode & attr) {
986 tsetdirt(i, i); 1007 tsetdirt(i, i);
987 break; 1008 break;
988 } 1009 }
989 } 1010 }
1011 y = (y+1) % TSCREEN.size;
990 } 1012 }
991} 1013}
992 1014
@@ -999,27 +1021,19 @@ tfulldirt(void)
999void 1021void
1000tcursor(int mode) 1022tcursor(int mode)
1001{ 1023{
1002 static TCursor c[2];
1003 int alt = IS_SET(MODE_ALTSCREEN);
1004
1005 if (mode == CURSOR_SAVE) { 1024 if (mode == CURSOR_SAVE) {
1006 c[alt] = term.c; 1025 TSCREEN.sc = term.c;
1007 } else if (mode == CURSOR_LOAD) { 1026 } else if (mode == CURSOR_LOAD) {
1008 term.c = c[alt]; 1027 term.c = TSCREEN.sc;
1009 tmoveto(c[alt].x, c[alt].y); 1028 tmoveto(term.c.x, term.c.y);
1010 } 1029 }
1011} 1030}
1012 1031
1013void 1032void
1014treset(void) 1033treset(void)
1015{ 1034{
1016 uint i; 1035 int i, j;
1017 1036 Glyph g = (Glyph){ .fg = defaultfg, .bg = defaultbg};
1018 term.c = (TCursor){{
1019 .mode = ATTR_NULL,
1020 .fg = defaultfg,
1021 .bg = defaultbg
1022 }, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
1023 1037
1024 memset(term.tabs, 0, term.col * sizeof(*term.tabs)); 1038 memset(term.tabs, 0, term.col * sizeof(*term.tabs));
1025 for (i = tabspaces; i < term.col; i += tabspaces) 1039 for (i = tabspaces; i < term.col; i += tabspaces)
@@ -1031,17 +1045,37 @@ treset(void)
1031 term.charset = 0; 1045 term.charset = 0;
1032 1046
1033 for (i = 0; i < 2; i++) { 1047 for (i = 0; i < 2; i++) {
1034 tmoveto(0, 0); 1048 term.screen[i].sc = (TCursor){{
1035 tcursor(CURSOR_SAVE); 1049 .fg = defaultfg,
1036 tclearregion(0, 0, term.col-1, term.row-1); 1050 .bg = defaultbg
1037 tswapscreen(); 1051 }};
1052 term.screen[i].cur = 0;
1053 term.screen[i].off = 0;
1054 for (j = 0; j < term.row; ++j) {
1055 if (term.col != term.linelen)
1056 term.screen[i].buffer[j] = xrealloc(term.screen[i].buffer[j], term.col * sizeof(Glyph));
1057 clearline(term.screen[i].buffer[j], g, 0, term.col);
1058 }
1059 for (j = term.row; j < term.screen[i].size; ++j) {
1060 free(term.screen[i].buffer[j]);
1061 term.screen[i].buffer[j] = NULL;
1062 }
1038 } 1063 }
1064 tcursor(CURSOR_LOAD);
1065 term.linelen = term.col;
1066 tfulldirt();
1039} 1067}
1040 1068
1041void 1069void
1042tnew(int col, int row) 1070tnew(int col, int row)
1043{ 1071{
1044 term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; 1072 int i;
1073 term = (Term){};
1074 term.screen[0].buffer = xmalloc(HISTSIZE * sizeof(Line));
1075 term.screen[0].size = HISTSIZE;
1076 term.screen[1].buffer = NULL;
1077 for (i = 0; i < HISTSIZE; ++i) term.screen[0].buffer[i] = NULL;
1078
1045 tresize(col, row); 1079 tresize(col, row);
1046 treset(); 1080 treset();
1047} 1081}
@@ -1049,15 +1083,43 @@ tnew(int col, int row)
1049void 1083void
1050tswapscreen(void) 1084tswapscreen(void)
1051{ 1085{
1052 Line *tmp = term.line;
1053
1054 term.line = term.alt;
1055 term.alt = tmp;
1056 term.mode ^= MODE_ALTSCREEN; 1086 term.mode ^= MODE_ALTSCREEN;
1057 tfulldirt(); 1087 tfulldirt();
1058} 1088}
1059 1089
1060void 1090void
1091kscrollup(const Arg *a)
1092{
1093 int n = a->i;
1094
1095 if (IS_SET(MODE_ALTSCREEN))
1096 return;
1097
1098 if (n < 0) n = (-n) * term.row;
1099 if (n > TSCREEN.size - term.row - TSCREEN.off) n = TSCREEN.size - term.row - TSCREEN.off;
1100 while (!TLINE(-n)) --n;
1101 TSCREEN.off += n;
1102 selscroll(0, n);
1103 tfulldirt();
1104}
1105
1106void
1107kscrolldown(const Arg *a)
1108{
1109
1110 int n = a->i;
1111
1112 if (IS_SET(MODE_ALTSCREEN))
1113 return;
1114
1115 if (n < 0) n = (-n) * term.row;
1116 if (n > TSCREEN.off) n = TSCREEN.off;
1117 TSCREEN.off -= n;
1118 selscroll(0, -n);
1119 tfulldirt();
1120}
1121
1122void
1061tscrolldown(int orig, int n) 1123tscrolldown(int orig, int n)
1062{ 1124{
1063 int i; 1125 int i;
@@ -1065,15 +1127,29 @@ tscrolldown(int orig, int n)
1065 1127
1066 LIMIT(n, 0, term.bot-orig+1); 1128 LIMIT(n, 0, term.bot-orig+1);
1067 1129
1068 tsetdirt(orig, term.bot-n); 1130 /* Ensure that lines are allocated */
1069 tclearregion(0, term.bot-n+1, term.col-1, term.bot); 1131 for (i = -n; i < 0; i++) {
1132 TLINE(i) = ensureline(TLINE(i));
1133 }
1070 1134
1071 for (i = term.bot; i >= orig+n; i--) { 1135 /* Shift non-scrolling areas in ring buffer */
1072 temp = term.line[i]; 1136 for (i = term.bot+1; i < term.row; i++) {
1073 term.line[i] = term.line[i-n]; 1137 temp = TLINE(i);
1074 term.line[i-n] = temp; 1138 TLINE(i) = TLINE(i-n);
1139 TLINE(i-n) = temp;
1140 }
1141 for (i = 0; i < orig; i++) {
1142 temp = TLINE(i);
1143 TLINE(i) = TLINE(i-n);
1144 TLINE(i-n) = temp;
1075 } 1145 }
1076 1146
1147 /* Scroll buffer */
1148 TSCREEN.cur = (TSCREEN.cur + TSCREEN.size - n) % TSCREEN.size;
1149 /* Clear lines that have entered the view */
1150 tclearregion(0, orig, term.linelen-1, orig+n-1);
1151 /* Redraw portion of the screen that has scrolled */
1152 tsetdirt(orig+n-1, term.bot);
1077 selscroll(orig, n); 1153 selscroll(orig, n);
1078} 1154}
1079 1155
@@ -1085,15 +1161,29 @@ tscrollup(int orig, int n)
1085 1161
1086 LIMIT(n, 0, term.bot-orig+1); 1162 LIMIT(n, 0, term.bot-orig+1);
1087 1163
1088 tclearregion(0, orig, term.col-1, orig+n-1); 1164 /* Ensure that lines are allocated */
1089 tsetdirt(orig+n, term.bot); 1165 for (i = term.row; i < term.row + n; i++) {
1166 TLINE(i) = ensureline(TLINE(i));
1167 }
1090 1168
1091 for (i = orig; i <= term.bot-n; i++) { 1169 /* Shift non-scrolling areas in ring buffer */
1092 temp = term.line[i]; 1170 for (i = orig-1; i >= 0; i--) {
1093 term.line[i] = term.line[i+n]; 1171 temp = TLINE(i);
1094 term.line[i+n] = temp; 1172 TLINE(i) = TLINE(i+n);
1173 TLINE(i+n) = temp;
1174 }
1175 for (i = term.row-1; i >term.bot; i--) {
1176 temp = TLINE(i);
1177 TLINE(i) = TLINE(i+n);
1178 TLINE(i+n) = temp;
1095 } 1179 }
1096 1180
1181 /* Scroll buffer */
1182 TSCREEN.cur = (TSCREEN.cur + n) % TSCREEN.size;
1183 /* Clear lines that have entered the view */
1184 tclearregion(0, term.bot-n+1, term.linelen-1, term.bot);
1185 /* Redraw portion of the screen that has scrolled */
1186 tsetdirt(orig, term.bot-n+1);
1097 selscroll(orig, -n); 1187 selscroll(orig, -n);
1098} 1188}
1099 1189
@@ -1200,6 +1290,7 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
1200 "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ 1290 "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */
1201 "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ 1291 "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */
1202 }; 1292 };
1293 Line line = TLINE(y);
1203 1294
1204 /* 1295 /*
1205 * The table is proudly stolen from rxvt. 1296 * The table is proudly stolen from rxvt.
@@ -1208,25 +1299,25 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
1208 BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) 1299 BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41])
1209 utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); 1300 utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ);
1210 1301
1211 if (term.line[y][x].mode & ATTR_WIDE) { 1302 if (line[x].mode & ATTR_WIDE) {
1212 if (x+1 < term.col) { 1303 if (x+1 < term.col) {
1213 term.line[y][x+1].u = ' '; 1304 line[x+1].u = ' ';
1214 term.line[y][x+1].mode &= ~ATTR_WDUMMY; 1305 line[x+1].mode &= ~ATTR_WDUMMY;
1215 } 1306 }
1216 } else if (term.line[y][x].mode & ATTR_WDUMMY) { 1307 } else if (line[x].mode & ATTR_WDUMMY) {
1217 term.line[y][x-1].u = ' '; 1308 line[x-1].u = ' ';
1218 term.line[y][x-1].mode &= ~ATTR_WIDE; 1309 line[x-1].mode &= ~ATTR_WIDE;
1219 } 1310 }
1220 1311
1221 term.dirty[y] = 1; 1312 term.dirty[y] = 1;
1222 term.line[y][x] = *attr; 1313 line[x] = *attr;
1223 term.line[y][x].u = u; 1314 line[x].u = u;
1224} 1315}
1225 1316
1226void 1317void
1227tclearregion(int x1, int y1, int x2, int y2) 1318tclearregion(int x1, int y1, int x2, int y2)
1228{ 1319{
1229 int x, y, temp; 1320 int x, y, L, S, temp;
1230 Glyph *gp; 1321 Glyph *gp;
1231 1322
1232 if (x1 > x2) 1323 if (x1 > x2)
@@ -1234,15 +1325,16 @@ tclearregion(int x1, int y1, int x2, int y2)
1234 if (y1 > y2) 1325 if (y1 > y2)
1235 temp = y1, y1 = y2, y2 = temp; 1326 temp = y1, y1 = y2, y2 = temp;
1236 1327
1237 LIMIT(x1, 0, term.col-1); 1328 LIMIT(x1, 0, term.linelen-1);
1238 LIMIT(x2, 0, term.col-1); 1329 LIMIT(x2, 0, term.linelen-1);
1239 LIMIT(y1, 0, term.row-1); 1330 LIMIT(y1, 0, term.row-1);
1240 LIMIT(y2, 0, term.row-1); 1331 LIMIT(y2, 0, term.row-1);
1241 1332
1333 L = TLINEOFFSET(y1);
1242 for (y = y1; y <= y2; y++) { 1334 for (y = y1; y <= y2; y++) {
1243 term.dirty[y] = 1; 1335 term.dirty[y] = 1;
1244 for (x = x1; x <= x2; x++) { 1336 for (x = x1; x <= x2; x++) {
1245 gp = &term.line[y][x]; 1337 gp = &TSCREEN.buffer[L][x];
1246 if (selected(x, y)) 1338 if (selected(x, y))
1247 selclear(); 1339 selclear();
1248 gp->fg = term.c.attr.fg; 1340 gp->fg = term.c.attr.fg;
@@ -1250,6 +1342,7 @@ tclearregion(int x1, int y1, int x2, int y2)
1250 gp->mode = 0; 1342 gp->mode = 0;
1251 gp->u = ' '; 1343 gp->u = ' ';
1252 } 1344 }
1345 L = (L + 1) % TSCREEN.size;
1253 } 1346 }
1254} 1347}
1255 1348
@@ -1264,7 +1357,7 @@ tdeletechar(int n)
1264 dst = term.c.x; 1357 dst = term.c.x;
1265 src = term.c.x + n; 1358 src = term.c.x + n;
1266 size = term.col - src; 1359 size = term.col - src;
1267 line = term.line[term.c.y]; 1360 line = TLINE(term.c.y);
1268 1361
1269 memmove(&line[dst], &line[src], size * sizeof(Glyph)); 1362 memmove(&line[dst], &line[src], size * sizeof(Glyph));
1270 tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); 1363 tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
@@ -1281,7 +1374,7 @@ tinsertblank(int n)
1281 dst = term.c.x + n; 1374 dst = term.c.x + n;
1282 src = term.c.x; 1375 src = term.c.x;
1283 size = term.col - dst; 1376 size = term.col - dst;
1284 line = term.line[term.c.y]; 1377 line = TLINE(term.c.y);
1285 1378
1286 memmove(&line[dst], &line[src], size * sizeof(Glyph)); 1379 memmove(&line[dst], &line[src], size * sizeof(Glyph));
1287 tclearregion(src, term.c.y, dst - 1, term.c.y); 1380 tclearregion(src, term.c.y, dst - 1, term.c.y);
@@ -2108,7 +2201,7 @@ tdumpline(int n)
2108 char buf[UTF_SIZ]; 2201 char buf[UTF_SIZ];
2109 const Glyph *bp, *end; 2202 const Glyph *bp, *end;
2110 2203
2111 bp = &term.line[n][0]; 2204 bp = &TLINE(n)[0];
2112 end = &bp[MIN(tlinelen(n), term.col) - 1]; 2205 end = &bp[MIN(tlinelen(n), term.col) - 1];
2113 if (bp != end || bp->u != ' ') { 2206 if (bp != end || bp->u != ' ') {
2114 for ( ; bp <= end; ++bp) 2207 for ( ; bp <= end; ++bp)
@@ -2496,11 +2589,11 @@ check_control_code:
2496 if (selected(term.c.x, term.c.y)) 2589 if (selected(term.c.x, term.c.y))
2497 selclear(); 2590 selclear();
2498 2591
2499 gp = &term.line[term.c.y][term.c.x]; 2592 gp = &TLINE(term.c.y)[term.c.x];
2500 if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { 2593 if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
2501 gp->mode |= ATTR_WRAP; 2594 gp->mode |= ATTR_WRAP;
2502 tnewline(1); 2595 tnewline(1);
2503 gp = &term.line[term.c.y][term.c.x]; 2596 gp = &TLINE(term.c.y)[term.c.x];
2504 } 2597 }
2505 2598
2506 if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) { 2599 if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) {
@@ -2513,7 +2606,7 @@ check_control_code:
2513 tnewline(1); 2606 tnewline(1);
2514 else 2607 else
2515 tmoveto(term.col - width, term.c.y); 2608 tmoveto(term.col - width, term.c.y);
2516 gp = &term.line[term.c.y][term.c.x]; 2609 gp = &TLINE(term.c.y)[term.c.x];
2517 } 2610 }
2518 2611
2519 tsetchar(u, &term.c.attr, term.c.x, term.c.y); 2612 tsetchar(u, &term.c.attr, term.c.x, term.c.y);
@@ -2544,6 +2637,11 @@ twrite(const char *buf, int buflen, int show_ctrl)
2544 Rune u; 2637 Rune u;
2545 int n; 2638 int n;
2546 2639
2640 if (TSCREEN.off) {
2641 TSCREEN.off = 0;
2642 tfulldirt();
2643 }
2644
2547 for (n = 0; n < buflen; n += charsize) { 2645 for (n = 0; n < buflen; n += charsize) {
2548 if (IS_SET(MODE_UTF8)) { 2646 if (IS_SET(MODE_UTF8)) {
2549 /* process a complete utf8 char */ 2647 /* process a complete utf8 char */
@@ -2570,56 +2668,85 @@ twrite(const char *buf, int buflen, int show_ctrl)
2570} 2668}
2571 2669
2572void 2670void
2573tresize(int col, int row) 2671clearline(Line line, Glyph g, int x, int xend)
2574{ 2672{
2575 int i; 2673 int i;
2674 g.mode = 0;
2675 g.u = ' ';
2676 for (i = x; i < xend; ++i) {
2677 line[i] = g;
2678 }
2679}
2680
2681Line
2682ensureline(Line line)
2683{
2684 if (!line) {
2685 line = xmalloc(term.linelen * sizeof(Glyph));
2686 }
2687 return line;
2688}
2689
2690void
2691tresize(int col, int row)
2692{
2693 int i, j;
2576 int minrow = MIN(row, term.row); 2694 int minrow = MIN(row, term.row);
2577 int mincol = MIN(col, term.col); 2695 int mincol = MIN(col, term.col);
2696 int linelen = MAX(col, term.linelen);
2578 int *bp; 2697 int *bp;
2579 TCursor c;
2580 2698
2581 if (col < 1 || row < 1) { 2699 if (col < 1 || row < 1 || row > HISTSIZE) {
2582 fprintf(stderr, 2700 fprintf(stderr,
2583 "tresize: error resizing to %dx%d\n", col, row); 2701 "tresize: error resizing to %dx%d\n", col, row);
2584 return; 2702 return;
2585 } 2703 }
2586 2704
2587 /* 2705 /* Shift buffer to keep the cursor where we expect it */
2588 * slide screen to keep cursor where we expect it - 2706 if (row <= term.c.y) {
2589 * tscrollup would work here, but we can optimize to 2707 term.screen[0].cur = (term.screen[0].cur - row + term.c.y + 1) % term.screen[0].size;
2590 * memmove because we're freeing the earlier lines 2708 }
2591 */ 2709
2592 for (i = 0; i <= term.c.y - row; i++) { 2710 /* Resize and clear line buffers as needed */
2593 free(term.line[i]); 2711 if (linelen > term.linelen) {
2594 free(term.alt[i]); 2712 for (i = 0; i < term.screen[0].size; ++i) {
2713 if (term.screen[0].buffer[i]) {
2714 term.screen[0].buffer[i] = xrealloc(term.screen[0].buffer[i], linelen * sizeof(Glyph));
2715 clearline(term.screen[0].buffer[i], term.c.attr, term.linelen, linelen);
2716 }
2717 }
2718 for (i = 0; i < minrow; ++i) {
2719 term.screen[1].buffer[i] = xrealloc(term.screen[1].buffer[i], linelen * sizeof(Glyph));
2720 clearline(term.screen[1].buffer[i], term.c.attr, term.linelen, linelen);
2721 }
2595 } 2722 }
2596 /* ensure that both src and dst are not NULL */ 2723 /* Allocate all visible lines for regular line buffer */
2597 if (i > 0) { 2724 for (j = term.screen[0].cur, i = 0; i < row; ++i, j = (j + 1) % term.screen[0].size)
2598 memmove(term.line, term.line + i, row * sizeof(Line)); 2725 {
2599 memmove(term.alt, term.alt + i, row * sizeof(Line)); 2726 if (!term.screen[0].buffer[j]) {
2727 term.screen[0].buffer[j] = xmalloc(linelen * sizeof(Glyph));
2728 }
2729 if (i >= term.row) {
2730 clearline(term.screen[0].buffer[j], term.c.attr, 0, linelen);
2731 }
2600 } 2732 }
2601 for (i += row; i < term.row; i++) { 2733 /* Resize alt screen */
2602 free(term.line[i]); 2734 term.screen[1].cur = 0;
2603 free(term.alt[i]); 2735 term.screen[1].size = row;
2736 for (i = row; i < term.row; ++i) {
2737 free(term.screen[1].buffer[i]);
2738 }
2739 term.screen[1].buffer = xrealloc(term.screen[1].buffer, row * sizeof(Line));
2740 for (i = term.row; i < row; ++i) {
2741 term.screen[1].buffer[i] = xmalloc(linelen * sizeof(Glyph));
2742 clearline(term.screen[1].buffer[i], term.c.attr, 0, linelen);
2604 } 2743 }
2605 2744
2606 /* resize to new height */ 2745 /* resize to new height */
2607 term.line = xrealloc(term.line, row * sizeof(Line));
2608 term.alt = xrealloc(term.alt, row * sizeof(Line));
2609 term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); 2746 term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
2610 term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); 2747 term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
2611 2748
2612 /* resize each row to new width, zero-pad if needed */ 2749 /* fix tabstops */
2613 for (i = 0; i < minrow; i++) {
2614 term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
2615 term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph));
2616 }
2617
2618 /* allocate any new rows */
2619 for (/* i = minrow */; i < row; i++) {
2620 term.line[i] = xmalloc(col * sizeof(Glyph));
2621 term.alt[i] = xmalloc(col * sizeof(Glyph));
2622 }
2623 if (col > term.col) { 2750 if (col > term.col) {
2624 bp = term.tabs + term.col; 2751 bp = term.tabs + term.col;
2625 2752
@@ -2629,26 +2756,16 @@ tresize(int col, int row)
2629 for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) 2756 for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
2630 *bp = 1; 2757 *bp = 1;
2631 } 2758 }
2759
2632 /* update terminal size */ 2760 /* update terminal size */
2633 term.col = col; 2761 term.col = col;
2634 term.row = row; 2762 term.row = row;
2763 term.linelen = linelen;
2635 /* reset scrolling region */ 2764 /* reset scrolling region */
2636 tsetscroll(0, row-1); 2765 tsetscroll(0, row-1);
2637 /* make use of the LIMIT in tmoveto */ 2766 /* make use of the LIMIT in tmoveto */
2638 tmoveto(term.c.x, term.c.y); 2767 tmoveto(term.c.x, term.c.y);
2639 /* Clearing both screens (it makes dirty all lines) */ 2768 tfulldirt();
2640 c = term.c;
2641 for (i = 0; i < 2; i++) {
2642 if (mincol < col && 0 < minrow) {
2643 tclearregion(mincol, 0, col - 1, minrow - 1);
2644 }
2645 if (0 < col && minrow < row) {
2646 tclearregion(0, minrow, col - 1, row - 1);
2647 }
2648 tswapscreen();
2649 tcursor(CURSOR_LOAD);
2650 }
2651 term.c = c;
2652} 2769}
2653 2770
2654void 2771void
@@ -2660,14 +2777,15 @@ resettitle(void)
2660void 2777void
2661drawregion(int x1, int y1, int x2, int y2) 2778drawregion(int x1, int y1, int x2, int y2)
2662{ 2779{
2663 int y; 2780 int y, L;
2664 2781
2782 L = TLINEOFFSET(y1);
2665 for (y = y1; y < y2; y++) { 2783 for (y = y1; y < y2; y++) {
2666 if (!term.dirty[y]) 2784 if (term.dirty[y]) {
2667 continue; 2785 term.dirty[y] = 0;
2668 2786 xdrawline(TSCREEN.buffer[L], x1, y, x2);
2669 term.dirty[y] = 0; 2787 }
2670 xdrawline(term.line[y], x1, y, x2); 2788 L = (L + 1) % TSCREEN.size;
2671 } 2789 }
2672} 2790}
2673 2791
@@ -2682,14 +2800,15 @@ draw(void)
2682 /* adjust cursor position */ 2800 /* adjust cursor position */
2683 LIMIT(term.ocx, 0, term.col-1); 2801 LIMIT(term.ocx, 0, term.col-1);
2684 LIMIT(term.ocy, 0, term.row-1); 2802 LIMIT(term.ocy, 0, term.row-1);
2685 if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY) 2803 if (TLINE(term.ocy)[term.ocx].mode & ATTR_WDUMMY)
2686 term.ocx--; 2804 term.ocx--;
2687 if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) 2805 if (TLINE(term.c.y)[cx].mode & ATTR_WDUMMY)
2688 cx--; 2806 cx--;
2689 2807
2690 drawregion(0, 0, term.col, term.row); 2808 drawregion(0, 0, term.col, term.row);
2691 xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], 2809 if (TSCREEN.off == 0)
2692 term.ocx, term.ocy, term.line[term.ocy][term.ocx]); 2810 xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
2811 term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]);
2693 term.ocx = cx; 2812 term.ocx = cx;
2694 term.ocy = term.c.y; 2813 term.ocy = term.c.y;
2695 xfinishdraw(); 2814 xfinishdraw();
diff --git a/st.h b/st.h
index fd3b0d8..3cea73b 100644
--- a/st.h
+++ b/st.h
@@ -19,6 +19,7 @@
19 19
20#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) 20#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
21#define IS_TRUECOL(x) (1 << 24 & (x)) 21#define IS_TRUECOL(x) (1 << 24 & (x))
22#define HISTSIZE 2000
22 23
23enum glyph_attribute { 24enum glyph_attribute {
24 ATTR_NULL = 0, 25 ATTR_NULL = 0,
diff --git a/x.c b/x.c
index d8aafee..d50c0c4 100644
--- a/x.c
+++ b/x.c
@@ -59,6 +59,8 @@ static void zoom(const Arg *);
59static void zoomabs(const Arg *); 59static void zoomabs(const Arg *);
60static void zoomreset(const Arg *); 60static void zoomreset(const Arg *);
61static void ttysend(const Arg *); 61static void ttysend(const Arg *);
62void kscrollup(const Arg *);
63void kscrolldown(const Arg *);
62 64
63/* config.h for applying patches and the configuration. */ 65/* config.h for applying patches and the configuration. */
64#include "config.h" 66#include "config.h"