diff options
| author | salmonik <raisersoft@proton.me> | 2026-02-05 23:51:23 +0100 |
|---|---|---|
| committer | salmonik <raisersoft@proton.me> | 2026-02-05 23:51:23 +0100 |
| commit | ae29d17209e8d22dff8a242369668a2f8f540046 (patch) | |
| tree | 30b33b901d0ac92bbdc6f4d0d505ec8072cc0cda | |
| parent | Add different cursor styles (diff) | |
| download | st-ae29d17209e8d22dff8a242369668a2f8f540046.tar.gz st-ae29d17209e8d22dff8a242369668a2f8f540046.zip | |
scrollback
| -rw-r--r-- | config.def.h | 2 | ||||
| -rw-r--r-- | st.c | 359 | ||||
| -rw-r--r-- | st.h | 1 | ||||
| -rw-r--r-- | x.c | 2 |
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 | /* |
| @@ -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 | |||
| 46 | enum term_mode { | 50 | enum 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 */ | ||
| 117 | typedef 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 */ |
| 113 | typedef struct { | 126 | typedef 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); | |||
| 203 | static void tstrsequence(uchar); | 216 | static void tstrsequence(uchar); |
| 204 | 217 | ||
| 205 | static void drawregion(int, int, int, int); | 218 | static void drawregion(int, int, int, int); |
| 219 | static void clearline(Line, Glyph, int, int); | ||
| 220 | static Line ensureline(Line); | ||
| 206 | 221 | ||
| 207 | static void selnormalize(void); | 222 | static void selnormalize(void); |
| 208 | static void selscroll(int, int); | 223 | static void selscroll(int, int); |
| @@ -408,11 +423,12 @@ int | |||
| 408 | tlinelen(int y) | 423 | tlinelen(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 | |||
| 949 | tattrset(int attr) | 965 | tattrset(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 | |||
| 979 | tsetdirtattr(int attr) | 998 | tsetdirtattr(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) | |||
| 999 | void | 1021 | void |
| 1000 | tcursor(int mode) | 1022 | tcursor(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 | ||
| 1013 | void | 1032 | void |
| 1014 | treset(void) | 1033 | treset(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 | ||
| 1041 | void | 1069 | void |
| 1042 | tnew(int col, int row) | 1070 | tnew(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) | |||
| 1049 | void | 1083 | void |
| 1050 | tswapscreen(void) | 1084 | tswapscreen(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 | ||
| 1060 | void | 1090 | void |
| 1091 | kscrollup(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 | |||
| 1106 | void | ||
| 1107 | kscrolldown(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 | |||
| 1122 | void | ||
| 1061 | tscrolldown(int orig, int n) | 1123 | tscrolldown(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 | ||
| 1226 | void | 1317 | void |
| 1227 | tclearregion(int x1, int y1, int x2, int y2) | 1318 | tclearregion(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 | ||
| 2572 | void | 2670 | void |
| 2573 | tresize(int col, int row) | 2671 | clearline(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 | |||
| 2681 | Line | ||
| 2682 | ensureline(Line line) | ||
| 2683 | { | ||
| 2684 | if (!line) { | ||
| 2685 | line = xmalloc(term.linelen * sizeof(Glyph)); | ||
| 2686 | } | ||
| 2687 | return line; | ||
| 2688 | } | ||
| 2689 | |||
| 2690 | void | ||
| 2691 | tresize(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 | ||
| 2654 | void | 2771 | void |
| @@ -2660,14 +2777,15 @@ resettitle(void) | |||
| 2660 | void | 2777 | void |
| 2661 | drawregion(int x1, int y1, int x2, int y2) | 2778 | drawregion(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(); |
| @@ -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 | ||
| 23 | enum glyph_attribute { | 24 | enum glyph_attribute { |
| 24 | ATTR_NULL = 0, | 25 | ATTR_NULL = 0, |
| @@ -59,6 +59,8 @@ static void zoom(const Arg *); | |||
| 59 | static void zoomabs(const Arg *); | 59 | static void zoomabs(const Arg *); |
| 60 | static void zoomreset(const Arg *); | 60 | static void zoomreset(const Arg *); |
| 61 | static void ttysend(const Arg *); | 61 | static void ttysend(const Arg *); |
| 62 | void kscrollup(const Arg *); | ||
| 63 | void 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" |
