scrollback_ringbuffer.diff 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  1. commit 0663bdf11a409961da5b1120741a69814da8ce65
  2. Author: Timo Röhling <timo@gaussglocke.de>
  3. Date: Tue Nov 23 19:45:33 2021 +0100
  4. Terminal scrollback with ring buffer
  5. This patch adds a ring buffer for scrollback to the terminal. The
  6. advantage of using a ring buffer is that the common case, scrolling with
  7. no static screen content, can be achieved very efficiently by
  8. incrementing and decrementing the starting line (modulo buffer size).
  9. The scrollback buffer is limited to HISTSIZE lines in order to bound
  10. memory usage. As the lines are allocated on demand, it is possible to
  11. implement unlimited scrollback with few changes. If the terminal is
  12. reset, the scroll back buffer is reset, too.
  13. diff --git a/config.def.h b/config.def.h
  14. index 2cd740a..8b25d40 100644
  15. --- a/config.def.h
  16. +++ b/config.def.h
  17. @@ -201,6 +201,8 @@ static Shortcut shortcuts[] = {
  18. { TERMMOD, XK_Y, selpaste, {.i = 0} },
  19. { ShiftMask, XK_Insert, selpaste, {.i = 0} },
  20. { TERMMOD, XK_Num_Lock, numlock, {.i = 0} },
  21. + { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} },
  22. + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} },
  23. };
  24. /*
  25. diff --git a/st.c b/st.c
  26. index b9f66e7..d9b163e 100644
  27. --- a/st.c
  28. +++ b/st.c
  29. @@ -43,6 +43,10 @@
  30. #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
  31. #define ISDELIM(u) (u && wcschr(worddelimiters, u))
  32. +#define TSCREEN term.screen[IS_SET(MODE_ALTSCREEN)]
  33. +#define TLINEOFFSET(y) (((y) + TSCREEN.cur - TSCREEN.off + TSCREEN.size) % TSCREEN.size)
  34. +#define TLINE(y) (TSCREEN.buffer[TLINEOFFSET(y)])
  35. +
  36. enum term_mode {
  37. MODE_WRAP = 1 << 0,
  38. MODE_INSERT = 1 << 1,
  39. @@ -109,12 +113,21 @@ typedef struct {
  40. int alt;
  41. } Selection;
  42. +/* Screen lines */
  43. +typedef struct {
  44. + Line* buffer; /* ring buffer */
  45. + int size; /* size of buffer */
  46. + int cur; /* start of active screen */
  47. + int off; /* scrollback line offset */
  48. + TCursor sc; /* saved cursor */
  49. +} LineBuffer;
  50. +
  51. /* Internal representation of the screen */
  52. typedef struct {
  53. int row; /* nb row */
  54. int col; /* nb col */
  55. - Line *line; /* screen */
  56. - Line *alt; /* alternate screen */
  57. + LineBuffer screen[2]; /* screen and alternate screen */
  58. + int linelen; /* allocated line length */
  59. int *dirty; /* dirtyness of lines */
  60. TCursor c; /* cursor */
  61. int ocx; /* old cursor col */
  62. @@ -203,6 +216,8 @@ static void tdeftran(char);
  63. static void tstrsequence(uchar);
  64. static void drawregion(int, int, int, int);
  65. +static void clearline(Line, Glyph, int, int);
  66. +static Line ensureline(Line);
  67. static void selnormalize(void);
  68. static void selscroll(int, int);
  69. @@ -408,11 +423,12 @@ int
  70. tlinelen(int y)
  71. {
  72. int i = term.col;
  73. + Line line = TLINE(y);
  74. - if (term.line[y][i - 1].mode & ATTR_WRAP)
  75. + if (line[i - 1].mode & ATTR_WRAP)
  76. return i;
  77. - while (i > 0 && term.line[y][i - 1].u == ' ')
  78. + while (i > 0 && line[i - 1].u == ' ')
  79. --i;
  80. return i;
  81. @@ -521,7 +537,7 @@ selsnap(int *x, int *y, int direction)
  82. * Snap around if the word wraps around at the end or
  83. * beginning of a line.
  84. */
  85. - prevgp = &term.line[*y][*x];
  86. + prevgp = &TLINE(*y)[*x];
  87. prevdelim = ISDELIM(prevgp->u);
  88. for (;;) {
  89. newx = *x + direction;
  90. @@ -536,14 +552,14 @@ selsnap(int *x, int *y, int direction)
  91. yt = *y, xt = *x;
  92. else
  93. yt = newy, xt = newx;
  94. - if (!(term.line[yt][xt].mode & ATTR_WRAP))
  95. + if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
  96. break;
  97. }
  98. if (newx >= tlinelen(newy))
  99. break;
  100. - gp = &term.line[newy][newx];
  101. + gp = &TLINE(newy)[newx];
  102. delim = ISDELIM(gp->u);
  103. if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
  104. || (delim && gp->u != prevgp->u)))
  105. @@ -564,14 +580,14 @@ selsnap(int *x, int *y, int direction)
  106. *x = (direction < 0) ? 0 : term.col - 1;
  107. if (direction < 0) {
  108. for (; *y > 0; *y += direction) {
  109. - if (!(term.line[*y-1][term.col-1].mode
  110. + if (!(TLINE(*y-1)[term.col-1].mode
  111. & ATTR_WRAP)) {
  112. break;
  113. }
  114. }
  115. } else if (direction > 0) {
  116. for (; *y < term.row-1; *y += direction) {
  117. - if (!(term.line[*y][term.col-1].mode
  118. + if (!(TLINE(*y)[term.col-1].mode
  119. & ATTR_WRAP)) {
  120. break;
  121. }
  122. @@ -602,13 +618,13 @@ getsel(void)
  123. }
  124. if (sel.type == SEL_RECTANGULAR) {
  125. - gp = &term.line[y][sel.nb.x];
  126. + gp = &TLINE(y)[sel.nb.x];
  127. lastx = sel.ne.x;
  128. } else {
  129. - gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
  130. + gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0];
  131. lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
  132. }
  133. - last = &term.line[y][MIN(lastx, linelen-1)];
  134. + last = &TLINE(y)[MIN(lastx, linelen-1)];
  135. while (last >= gp && last->u == ' ')
  136. --last;
  137. @@ -949,12 +965,15 @@ int
  138. tattrset(int attr)
  139. {
  140. int i, j;
  141. + int y = TLINEOFFSET(0);
  142. for (i = 0; i < term.row-1; i++) {
  143. + Line line = TSCREEN.buffer[y];
  144. for (j = 0; j < term.col-1; j++) {
  145. - if (term.line[i][j].mode & attr)
  146. + if (line[j].mode & attr)
  147. return 1;
  148. }
  149. + y = (y+1) % TSCREEN.size;
  150. }
  151. return 0;
  152. @@ -976,14 +995,17 @@ void
  153. tsetdirtattr(int attr)
  154. {
  155. int i, j;
  156. + int y = TLINEOFFSET(0);
  157. for (i = 0; i < term.row-1; i++) {
  158. + Line line = TSCREEN.buffer[y];
  159. for (j = 0; j < term.col-1; j++) {
  160. - if (term.line[i][j].mode & attr) {
  161. + if (line[j].mode & attr) {
  162. tsetdirt(i, i);
  163. break;
  164. }
  165. }
  166. + y = (y+1) % TSCREEN.size;
  167. }
  168. }
  169. @@ -996,27 +1018,19 @@ tfulldirt(void)
  170. void
  171. tcursor(int mode)
  172. {
  173. - static TCursor c[2];
  174. - int alt = IS_SET(MODE_ALTSCREEN);
  175. -
  176. if (mode == CURSOR_SAVE) {
  177. - c[alt] = term.c;
  178. + TSCREEN.sc = term.c;
  179. } else if (mode == CURSOR_LOAD) {
  180. - term.c = c[alt];
  181. - tmoveto(c[alt].x, c[alt].y);
  182. + term.c = TSCREEN.sc;
  183. + tmoveto(term.c.x, term.c.y);
  184. }
  185. }
  186. void
  187. treset(void)
  188. {
  189. - uint i;
  190. -
  191. - term.c = (TCursor){{
  192. - .mode = ATTR_NULL,
  193. - .fg = defaultfg,
  194. - .bg = defaultbg
  195. - }, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
  196. + int i, j;
  197. + Glyph g = (Glyph){ .fg = defaultfg, .bg = defaultbg};
  198. memset(term.tabs, 0, term.col * sizeof(*term.tabs));
  199. for (i = tabspaces; i < term.col; i += tabspaces)
  200. @@ -1028,17 +1042,37 @@ treset(void)
  201. term.charset = 0;
  202. for (i = 0; i < 2; i++) {
  203. - tmoveto(0, 0);
  204. - tcursor(CURSOR_SAVE);
  205. - tclearregion(0, 0, term.col-1, term.row-1);
  206. - tswapscreen();
  207. + term.screen[i].sc = (TCursor){{
  208. + .fg = defaultfg,
  209. + .bg = defaultbg
  210. + }};
  211. + term.screen[i].cur = 0;
  212. + term.screen[i].off = 0;
  213. + for (j = 0; j < term.row; ++j) {
  214. + if (term.col != term.linelen)
  215. + term.screen[i].buffer[j] = xrealloc(term.screen[i].buffer[j], term.col * sizeof(Glyph));
  216. + clearline(term.screen[i].buffer[j], g, 0, term.col);
  217. + }
  218. + for (j = term.row; j < term.screen[i].size; ++j) {
  219. + free(term.screen[i].buffer[j]);
  220. + term.screen[i].buffer[j] = NULL;
  221. + }
  222. }
  223. + tcursor(CURSOR_LOAD);
  224. + term.linelen = term.col;
  225. + tfulldirt();
  226. }
  227. void
  228. tnew(int col, int row)
  229. {
  230. - term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };
  231. + int i;
  232. + term = (Term){};
  233. + term.screen[0].buffer = xmalloc(HISTSIZE * sizeof(Line));
  234. + term.screen[0].size = HISTSIZE;
  235. + term.screen[1].buffer = NULL;
  236. + for (i = 0; i < HISTSIZE; ++i) term.screen[0].buffer[i] = NULL;
  237. +
  238. tresize(col, row);
  239. treset();
  240. }
  241. @@ -1046,14 +1080,42 @@ tnew(int col, int row)
  242. void
  243. tswapscreen(void)
  244. {
  245. - Line *tmp = term.line;
  246. -
  247. - term.line = term.alt;
  248. - term.alt = tmp;
  249. term.mode ^= MODE_ALTSCREEN;
  250. tfulldirt();
  251. }
  252. +void
  253. +kscrollup(const Arg *a)
  254. +{
  255. + int n = a->i;
  256. +
  257. + if (IS_SET(MODE_ALTSCREEN))
  258. + return;
  259. +
  260. + if (n < 0) n = (-n) * term.row;
  261. + if (n > TSCREEN.size - term.row - TSCREEN.off) n = TSCREEN.size - term.row - TSCREEN.off;
  262. + while (!TLINE(-n)) --n;
  263. + TSCREEN.off += n;
  264. + selscroll(0, n);
  265. + tfulldirt();
  266. +}
  267. +
  268. +void
  269. +kscrolldown(const Arg *a)
  270. +{
  271. +
  272. + int n = a->i;
  273. +
  274. + if (IS_SET(MODE_ALTSCREEN))
  275. + return;
  276. +
  277. + if (n < 0) n = (-n) * term.row;
  278. + if (n > TSCREEN.off) n = TSCREEN.off;
  279. + TSCREEN.off -= n;
  280. + selscroll(0, -n);
  281. + tfulldirt();
  282. +}
  283. +
  284. void
  285. tscrolldown(int orig, int n)
  286. {
  287. @@ -1062,15 +1124,29 @@ tscrolldown(int orig, int n)
  288. LIMIT(n, 0, term.bot-orig+1);
  289. - tsetdirt(orig, term.bot-n);
  290. - tclearregion(0, term.bot-n+1, term.col-1, term.bot);
  291. + /* Ensure that lines are allocated */
  292. + for (i = -n; i < 0; i++) {
  293. + TLINE(i) = ensureline(TLINE(i));
  294. + }
  295. - for (i = term.bot; i >= orig+n; i--) {
  296. - temp = term.line[i];
  297. - term.line[i] = term.line[i-n];
  298. - term.line[i-n] = temp;
  299. + /* Shift non-scrolling areas in ring buffer */
  300. + for (i = term.bot+1; i < term.row; i++) {
  301. + temp = TLINE(i);
  302. + TLINE(i) = TLINE(i-n);
  303. + TLINE(i-n) = temp;
  304. + }
  305. + for (i = 0; i < orig; i++) {
  306. + temp = TLINE(i);
  307. + TLINE(i) = TLINE(i-n);
  308. + TLINE(i-n) = temp;
  309. }
  310. + /* Scroll buffer */
  311. + TSCREEN.cur = (TSCREEN.cur + TSCREEN.size - n) % TSCREEN.size;
  312. + /* Clear lines that have entered the view */
  313. + tclearregion(0, orig, term.linelen-1, orig+n-1);
  314. + /* Redraw portion of the screen that has scrolled */
  315. + tsetdirt(orig+n-1, term.bot);
  316. selscroll(orig, n);
  317. }
  318. @@ -1082,15 +1158,29 @@ tscrollup(int orig, int n)
  319. LIMIT(n, 0, term.bot-orig+1);
  320. - tclearregion(0, orig, term.col-1, orig+n-1);
  321. - tsetdirt(orig+n, term.bot);
  322. + /* Ensure that lines are allocated */
  323. + for (i = term.row; i < term.row + n; i++) {
  324. + TLINE(i) = ensureline(TLINE(i));
  325. + }
  326. - for (i = orig; i <= term.bot-n; i++) {
  327. - temp = term.line[i];
  328. - term.line[i] = term.line[i+n];
  329. - term.line[i+n] = temp;
  330. + /* Shift non-scrolling areas in ring buffer */
  331. + for (i = orig-1; i >= 0; i--) {
  332. + temp = TLINE(i);
  333. + TLINE(i) = TLINE(i+n);
  334. + TLINE(i+n) = temp;
  335. + }
  336. + for (i = term.row-1; i >term.bot; i--) {
  337. + temp = TLINE(i);
  338. + TLINE(i) = TLINE(i+n);
  339. + TLINE(i+n) = temp;
  340. }
  341. + /* Scroll buffer */
  342. + TSCREEN.cur = (TSCREEN.cur + n) % TSCREEN.size;
  343. + /* Clear lines that have entered the view */
  344. + tclearregion(0, term.bot-n+1, term.linelen-1, term.bot);
  345. + /* Redraw portion of the screen that has scrolled */
  346. + tsetdirt(orig, term.bot-n+1);
  347. selscroll(orig, -n);
  348. }
  349. @@ -1194,6 +1284,7 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
  350. "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */
  351. "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */
  352. };
  353. + Line line = TLINE(y);
  354. /*
  355. * The table is proudly stolen from rxvt.
  356. @@ -1202,25 +1293,25 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
  357. BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41])
  358. utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ);
  359. - if (term.line[y][x].mode & ATTR_WIDE) {
  360. + if (line[x].mode & ATTR_WIDE) {
  361. if (x+1 < term.col) {
  362. - term.line[y][x+1].u = ' ';
  363. - term.line[y][x+1].mode &= ~ATTR_WDUMMY;
  364. + line[x+1].u = ' ';
  365. + line[x+1].mode &= ~ATTR_WDUMMY;
  366. }
  367. - } else if (term.line[y][x].mode & ATTR_WDUMMY) {
  368. - term.line[y][x-1].u = ' ';
  369. - term.line[y][x-1].mode &= ~ATTR_WIDE;
  370. + } else if (line[x].mode & ATTR_WDUMMY) {
  371. + line[x-1].u = ' ';
  372. + line[x-1].mode &= ~ATTR_WIDE;
  373. }
  374. term.dirty[y] = 1;
  375. - term.line[y][x] = *attr;
  376. - term.line[y][x].u = u;
  377. + line[x] = *attr;
  378. + line[x].u = u;
  379. }
  380. void
  381. tclearregion(int x1, int y1, int x2, int y2)
  382. {
  383. - int x, y, temp;
  384. + int x, y, L, S, temp;
  385. Glyph *gp;
  386. if (x1 > x2)
  387. @@ -1228,15 +1319,16 @@ tclearregion(int x1, int y1, int x2, int y2)
  388. if (y1 > y2)
  389. temp = y1, y1 = y2, y2 = temp;
  390. - LIMIT(x1, 0, term.col-1);
  391. - LIMIT(x2, 0, term.col-1);
  392. + LIMIT(x1, 0, term.linelen-1);
  393. + LIMIT(x2, 0, term.linelen-1);
  394. LIMIT(y1, 0, term.row-1);
  395. LIMIT(y2, 0, term.row-1);
  396. + L = TLINEOFFSET(y1);
  397. for (y = y1; y <= y2; y++) {
  398. term.dirty[y] = 1;
  399. for (x = x1; x <= x2; x++) {
  400. - gp = &term.line[y][x];
  401. + gp = &TSCREEN.buffer[L][x];
  402. if (selected(x, y))
  403. selclear();
  404. gp->fg = term.c.attr.fg;
  405. @@ -1244,6 +1336,7 @@ tclearregion(int x1, int y1, int x2, int y2)
  406. gp->mode = 0;
  407. gp->u = ' ';
  408. }
  409. + L = (L + 1) % TSCREEN.size;
  410. }
  411. }
  412. @@ -1258,7 +1351,7 @@ tdeletechar(int n)
  413. dst = term.c.x;
  414. src = term.c.x + n;
  415. size = term.col - src;
  416. - line = term.line[term.c.y];
  417. + line = TLINE(term.c.y);
  418. memmove(&line[dst], &line[src], size * sizeof(Glyph));
  419. tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
  420. @@ -1275,7 +1368,7 @@ tinsertblank(int n)
  421. dst = term.c.x + n;
  422. src = term.c.x;
  423. size = term.col - dst;
  424. - line = term.line[term.c.y];
  425. + line = TLINE(term.c.y);
  426. memmove(&line[dst], &line[src], size * sizeof(Glyph));
  427. tclearregion(src, term.c.y, dst - 1, term.c.y);
  428. @@ -2079,7 +2172,7 @@ tdumpline(int n)
  429. char buf[UTF_SIZ];
  430. const Glyph *bp, *end;
  431. - bp = &term.line[n][0];
  432. + bp = &TLINE(n)[0];
  433. end = &bp[MIN(tlinelen(n), term.col) - 1];
  434. if (bp != end || bp->u != ' ') {
  435. for ( ; bp <= end; ++bp)
  436. @@ -2466,11 +2559,11 @@ check_control_code:
  437. if (selected(term.c.x, term.c.y))
  438. selclear();
  439. - gp = &term.line[term.c.y][term.c.x];
  440. + gp = &TLINE(term.c.y)[term.c.x];
  441. if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
  442. gp->mode |= ATTR_WRAP;
  443. tnewline(1);
  444. - gp = &term.line[term.c.y][term.c.x];
  445. + gp = &TLINE(term.c.y)[term.c.x];
  446. }
  447. if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) {
  448. @@ -2483,7 +2576,7 @@ check_control_code:
  449. tnewline(1);
  450. else
  451. tmoveto(term.col - width, term.c.y);
  452. - gp = &term.line[term.c.y][term.c.x];
  453. + gp = &TLINE(term.c.y)[term.c.x];
  454. }
  455. tsetchar(u, &term.c.attr, term.c.x, term.c.y);
  456. @@ -2514,6 +2607,11 @@ twrite(const char *buf, int buflen, int show_ctrl)
  457. Rune u;
  458. int n;
  459. + if (TSCREEN.off) {
  460. + TSCREEN.off = 0;
  461. + tfulldirt();
  462. + }
  463. +
  464. for (n = 0; n < buflen; n += charsize) {
  465. if (IS_SET(MODE_UTF8)) {
  466. /* process a complete utf8 char */
  467. @@ -2540,56 +2638,85 @@ twrite(const char *buf, int buflen, int show_ctrl)
  468. }
  469. void
  470. -tresize(int col, int row)
  471. +clearline(Line line, Glyph g, int x, int xend)
  472. {
  473. int i;
  474. + g.mode = 0;
  475. + g.u = ' ';
  476. + for (i = x; i < xend; ++i) {
  477. + line[i] = g;
  478. + }
  479. +}
  480. +
  481. +Line
  482. +ensureline(Line line)
  483. +{
  484. + if (!line) {
  485. + line = xmalloc(term.linelen * sizeof(Glyph));
  486. + }
  487. + return line;
  488. +}
  489. +
  490. +void
  491. +tresize(int col, int row)
  492. +{
  493. + int i, j;
  494. int minrow = MIN(row, term.row);
  495. int mincol = MIN(col, term.col);
  496. + int linelen = MAX(col, term.linelen);
  497. int *bp;
  498. - TCursor c;
  499. - if (col < 1 || row < 1) {
  500. + if (col < 1 || row < 1 || row > HISTSIZE) {
  501. fprintf(stderr,
  502. "tresize: error resizing to %dx%d\n", col, row);
  503. return;
  504. }
  505. - /*
  506. - * slide screen to keep cursor where we expect it -
  507. - * tscrollup would work here, but we can optimize to
  508. - * memmove because we're freeing the earlier lines
  509. - */
  510. - for (i = 0; i <= term.c.y - row; i++) {
  511. - free(term.line[i]);
  512. - free(term.alt[i]);
  513. + /* Shift buffer to keep the cursor where we expect it */
  514. + if (row <= term.c.y) {
  515. + term.screen[0].cur = (term.screen[0].cur - row + term.c.y + 1) % term.screen[0].size;
  516. + }
  517. +
  518. + /* Resize and clear line buffers as needed */
  519. + if (linelen > term.linelen) {
  520. + for (i = 0; i < term.screen[0].size; ++i) {
  521. + if (term.screen[0].buffer[i]) {
  522. + term.screen[0].buffer[i] = xrealloc(term.screen[0].buffer[i], linelen * sizeof(Glyph));
  523. + clearline(term.screen[0].buffer[i], term.c.attr, term.linelen, linelen);
  524. + }
  525. + }
  526. + for (i = 0; i < minrow; ++i) {
  527. + term.screen[1].buffer[i] = xrealloc(term.screen[1].buffer[i], linelen * sizeof(Glyph));
  528. + clearline(term.screen[1].buffer[i], term.c.attr, term.linelen, linelen);
  529. + }
  530. }
  531. - /* ensure that both src and dst are not NULL */
  532. - if (i > 0) {
  533. - memmove(term.line, term.line + i, row * sizeof(Line));
  534. - memmove(term.alt, term.alt + i, row * sizeof(Line));
  535. + /* Allocate all visible lines for regular line buffer */
  536. + for (j = term.screen[0].cur, i = 0; i < row; ++i, j = (j + 1) % term.screen[0].size)
  537. + {
  538. + if (!term.screen[0].buffer[j]) {
  539. + term.screen[0].buffer[j] = xmalloc(linelen * sizeof(Glyph));
  540. + }
  541. + if (i >= term.row) {
  542. + clearline(term.screen[0].buffer[j], term.c.attr, 0, linelen);
  543. + }
  544. }
  545. - for (i += row; i < term.row; i++) {
  546. - free(term.line[i]);
  547. - free(term.alt[i]);
  548. + /* Resize alt screen */
  549. + term.screen[1].cur = 0;
  550. + term.screen[1].size = row;
  551. + for (i = row; i < term.row; ++i) {
  552. + free(term.screen[1].buffer[i]);
  553. + }
  554. + term.screen[1].buffer = xrealloc(term.screen[1].buffer, row * sizeof(Line));
  555. + for (i = term.row; i < row; ++i) {
  556. + term.screen[1].buffer[i] = xmalloc(linelen * sizeof(Glyph));
  557. + clearline(term.screen[1].buffer[i], term.c.attr, 0, linelen);
  558. }
  559. /* resize to new height */
  560. - term.line = xrealloc(term.line, row * sizeof(Line));
  561. - term.alt = xrealloc(term.alt, row * sizeof(Line));
  562. term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
  563. term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
  564. - /* resize each row to new width, zero-pad if needed */
  565. - for (i = 0; i < minrow; i++) {
  566. - term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
  567. - term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph));
  568. - }
  569. -
  570. - /* allocate any new rows */
  571. - for (/* i = minrow */; i < row; i++) {
  572. - term.line[i] = xmalloc(col * sizeof(Glyph));
  573. - term.alt[i] = xmalloc(col * sizeof(Glyph));
  574. - }
  575. + /* fix tabstops */
  576. if (col > term.col) {
  577. bp = term.tabs + term.col;
  578. @@ -2599,26 +2726,16 @@ tresize(int col, int row)
  579. for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
  580. *bp = 1;
  581. }
  582. +
  583. /* update terminal size */
  584. term.col = col;
  585. term.row = row;
  586. + term.linelen = linelen;
  587. /* reset scrolling region */
  588. tsetscroll(0, row-1);
  589. /* make use of the LIMIT in tmoveto */
  590. tmoveto(term.c.x, term.c.y);
  591. - /* Clearing both screens (it makes dirty all lines) */
  592. - c = term.c;
  593. - for (i = 0; i < 2; i++) {
  594. - if (mincol < col && 0 < minrow) {
  595. - tclearregion(mincol, 0, col - 1, minrow - 1);
  596. - }
  597. - if (0 < col && minrow < row) {
  598. - tclearregion(0, minrow, col - 1, row - 1);
  599. - }
  600. - tswapscreen();
  601. - tcursor(CURSOR_LOAD);
  602. - }
  603. - term.c = c;
  604. + tfulldirt();
  605. }
  606. void
  607. @@ -2630,14 +2747,15 @@ resettitle(void)
  608. void
  609. drawregion(int x1, int y1, int x2, int y2)
  610. {
  611. - int y;
  612. + int y, L;
  613. + L = TLINEOFFSET(y1);
  614. for (y = y1; y < y2; y++) {
  615. - if (!term.dirty[y])
  616. - continue;
  617. -
  618. - term.dirty[y] = 0;
  619. - xdrawline(term.line[y], x1, y, x2);
  620. + if (term.dirty[y]) {
  621. + term.dirty[y] = 0;
  622. + xdrawline(TSCREEN.buffer[L], x1, y, x2);
  623. + }
  624. + L = (L + 1) % TSCREEN.size;
  625. }
  626. }
  627. @@ -2652,14 +2770,15 @@ draw(void)
  628. /* adjust cursor position */
  629. LIMIT(term.ocx, 0, term.col-1);
  630. LIMIT(term.ocy, 0, term.row-1);
  631. - if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY)
  632. + if (TLINE(term.ocy)[term.ocx].mode & ATTR_WDUMMY)
  633. term.ocx--;
  634. - if (term.line[term.c.y][cx].mode & ATTR_WDUMMY)
  635. + if (TLINE(term.c.y)[cx].mode & ATTR_WDUMMY)
  636. cx--;
  637. drawregion(0, 0, term.col, term.row);
  638. - xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
  639. - term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
  640. + if (TSCREEN.off == 0)
  641. + xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
  642. + term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]);
  643. term.ocx = cx;
  644. term.ocy = term.c.y;
  645. xfinishdraw();
  646. diff --git a/st.h b/st.h
  647. index fd3b0d8..3cea73b 100644
  648. --- a/st.h
  649. +++ b/st.h
  650. @@ -19,6 +19,7 @@
  651. #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
  652. #define IS_TRUECOL(x) (1 << 24 & (x))
  653. +#define HISTSIZE 2000
  654. enum glyph_attribute {
  655. ATTR_NULL = 0,
  656. diff --git a/x.c b/x.c
  657. index bd23686..25785a6 100644
  658. --- a/x.c
  659. +++ b/x.c
  660. @@ -59,6 +59,8 @@ static void zoom(const Arg *);
  661. static void zoomabs(const Arg *);
  662. static void zoomreset(const Arg *);
  663. static void ttysend(const Arg *);
  664. +void kscrollup(const Arg *);
  665. +void kscrolldown(const Arg *);
  666. /* config.h for applying patches and the configuration. */
  667. #include "config.h"