1 diff --git a/config.def.h b/config.def.h
2 index bb5596e..2df4cbc 100644
7 static char font[] = "Liberation Mono:pixelsize=12:antialias=false:autohint=false";
8 static int borderpx = 2;
9 +static int histsize = 2000;
10 static char shell[] = "/bin/sh";
11 static char *utmp = NULL;
12 static char stty_args[] = "stty raw -echo -iexten echonl";
13 @@ -128,6 +129,8 @@ static Shortcut shortcuts[] = {
14 { MODKEY|ShiftMask, XK_C, clipcopy, {.i = 0} },
15 { MODKEY|ShiftMask, XK_V, clippaste, {.i = 0} },
16 { MODKEY, XK_Num_Lock, numlock, {.i = 0} },
17 + { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} },
18 + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} },
22 diff --git a/st.c b/st.c
23 index 0c6b9c3..ab56435 100644
26 @@ -83,6 +83,8 @@ char *argv0;
27 #define TRUERED(x) (((x) & 0xff0000) >> 8)
28 #define TRUEGREEN(x) (((x) & 0xff00))
29 #define TRUEBLUE(x) (((x) & 0xff) << 8)
30 +#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - term.scr \
31 + + histsize + 1) % histsize] : term.line[(y) - term.scr])
34 enum glyph_attribute {
35 @@ -231,6 +233,9 @@ typedef struct {
37 Line *line; /* screen */
38 Line *alt; /* alternate screen */
39 + Line *hist; /* history buffer */
40 + int histi; /* history index */
41 + int scr; /* scroll back */
42 bool *dirty; /* dirtyness of lines */
43 XftGlyphFontSpec *specbuf; /* font spec buffer used for rendering */
44 TCursor c; /* cursor */
45 @@ -324,6 +329,8 @@ typedef struct {
46 /* function definitions used in config.h */
47 static void clipcopy(const Arg *);
48 static void clippaste(const Arg *);
49 +static void kscrolldown(const Arg *);
50 +static void kscrollup(const Arg *);
51 static void numlock(const Arg *);
52 static void selpaste(const Arg *);
53 static void xzoom(const Arg *);
54 @@ -395,8 +402,8 @@ static void tputtab(int);
55 static void tputc(Rune);
56 static void treset(void);
57 static void tresize(int, int);
58 -static void tscrollup(int, int);
59 -static void tscrolldown(int, int);
60 +static void tscrollup(int, int, bool);
61 +static void tscrolldown(int, int, bool);
62 static void tsetattr(int *, int);
63 static void tsetchar(Rune, Glyph *, int, int);
64 static void tsetscroll(int, int);
65 @@ -682,10 +689,10 @@ int
69 - if(term.line[y][i - 1].mode & ATTR_WRAP)
70 + if(TLINE(y)[i - 1].mode & ATTR_WRAP)
73 - while(i > 0 && term.line[y][i - 1].u == ' ')
74 + while(i > 0 && TLINE(y)[i - 1].u == ' ')
78 @@ -744,7 +751,7 @@ selsnap(int *x, int *y, int direction) {
79 * Snap around if the word wraps around at the end or
80 * beginning of a line.
82 - prevgp = &term.line[*y][*x];
83 + prevgp = &TLINE(*y)[*x];
84 prevdelim = ISDELIM(prevgp->u);
86 newx = *x + direction;
87 @@ -759,14 +766,14 @@ selsnap(int *x, int *y, int direction) {
91 - if(!(term.line[yt][xt].mode & ATTR_WRAP))
92 + if(!(TLINE(yt)[xt].mode & ATTR_WRAP))
96 if (newx >= tlinelen(newy))
99 - gp = &term.line[newy][newx];
100 + gp = &TLINE(newy)[newx];
101 delim = ISDELIM(gp->u);
102 if(!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
103 || (delim && gp->u != prevgp->u)))
104 @@ -787,14 +794,14 @@ selsnap(int *x, int *y, int direction) {
105 *x = (direction < 0) ? 0 : term.col - 1;
107 for(; *y > 0; *y += direction) {
108 - if(!(term.line[*y-1][term.col-1].mode
109 + if(!(TLINE(*y-1)[term.col-1].mode
114 } else if(direction > 0) {
115 for(; *y < term.row-1; *y += direction) {
116 - if(!(term.line[*y][term.col-1].mode
117 + if(!(TLINE(*y)[term.col-1].mode
121 @@ -953,13 +960,13 @@ getsel(void) {
122 linelen = tlinelen(y);
124 if(sel.type == SEL_RECTANGULAR) {
125 - gp = &term.line[y][sel.nb.x];
126 + gp = &TLINE(y)[sel.nb.x];
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;
133 - last = &term.line[y][MIN(lastx, linelen-1)];
134 + last = &TLINE(y)[MIN(lastx, linelen-1)];
135 while(last >= gp && last->u == ' ')
138 @@ -1362,10 +1369,16 @@ ttyread(void) {
140 /* keep any uncomplete utf8 char for the next call */
141 memmove(buf, ptr, buflen);
142 + if(term.scr > 0 && term.scr < histsize-1)
147 ttywrite(const char *s, size_t n) {
148 + Arg arg = (Arg){ .i = term.scr };
152 if(xwrite(cmdfd, s, n) == -1)
153 die("write error on tty: %s\n", strerror(errno));
155 @@ -1500,13 +1513,52 @@ tswapscreen(void) {
159 -tscrolldown(int orig, int n) {
160 +kscrolldown(const Arg* a) {
177 +kscrollup(const Arg* a) {
183 + if(term.scr <= histsize - n) {
191 +tscrolldown(int orig, int n, bool copyhist) {
195 LIMIT(n, 0, term.bot-orig+1);
198 + term.histi = (term.histi - 1 + histsize) % histsize;
199 + temp = term.hist[term.histi];
200 + term.hist[term.histi] = term.line[term.bot];
201 + term.line[term.bot] = temp;
204 tsetdirt(orig, term.bot-n);
206 tclearregion(0, term.bot-n+1, term.col-1, term.bot);
208 for(i = term.bot; i >= orig+n; i--) {
209 @@ -1519,12 +1571,19 @@ tscrolldown(int orig, int n) {
213 -tscrollup(int orig, int n) {
214 +tscrollup(int orig, int n, bool copyhist) {
218 LIMIT(n, 0, term.bot-orig+1);
221 + term.histi = (term.histi + 1) % histsize;
222 + temp = term.hist[term.histi];
223 + term.hist[term.histi] = term.line[orig];
224 + term.line[orig] = temp;
227 tclearregion(0, orig, term.col-1, orig+n-1);
228 tsetdirt(orig+n, term.bot);
230 @@ -1571,7 +1630,7 @@ tnewline(int first_col) {
234 - tscrollup(term.top, 1);
235 + tscrollup(term.top, 1, true);
239 @@ -1728,13 +1787,13 @@ tinsertblank(int n) {
241 tinsertblankline(int n) {
242 if(BETWEEN(term.c.y, term.top, term.bot))
243 - tscrolldown(term.c.y, n);
244 + tscrolldown(term.c.y, n, false);
249 if(BETWEEN(term.c.y, term.top, term.bot))
250 - tscrollup(term.c.y, n);
251 + tscrollup(term.c.y, n, false);
255 @@ -2163,11 +2222,11 @@ csihandle(void) {
257 case 'S': /* SU -- Scroll <n> line up */
258 DEFAULT(csiescseq.arg[0], 1);
259 - tscrollup(term.top, csiescseq.arg[0]);
260 + tscrollup(term.top, csiescseq.arg[0], false);
262 case 'T': /* SD -- Scroll <n> line down */
263 DEFAULT(csiescseq.arg[0], 1);
264 - tscrolldown(term.top, csiescseq.arg[0]);
265 + tscrolldown(term.top, csiescseq.arg[0], false);
267 case 'L': /* IL -- Insert <n> blank lines */
268 DEFAULT(csiescseq.arg[0], 1);
269 @@ -2617,7 +2676,7 @@ eschandle(uchar ascii) {
271 case 'D': /* IND -- Linefeed */
272 if(term.c.y == term.bot) {
273 - tscrollup(term.top, 1);
274 + tscrollup(term.top, 1, true);
276 tmoveto(term.c.x, term.c.y+1);
278 @@ -2630,7 +2689,7 @@ eschandle(uchar ascii) {
280 case 'M': /* RI -- Reverse index */
281 if(term.c.y == term.top) {
282 - tscrolldown(term.top, 1);
283 + tscrolldown(term.top, 1, true);
285 tmoveto(term.c.x, term.c.y-1);
287 @@ -2791,7 +2850,7 @@ tputc(Rune u) {
290 tresize(int col, int row) {
293 int minrow = MIN(row, term.row);
294 int mincol = MIN(col, term.col);
296 @@ -2828,9 +2887,18 @@ tresize(int col, int row) {
297 /* resize to new height */
298 term.line = xrealloc(term.line, row * sizeof(Line));
299 term.alt = xrealloc(term.alt, row * sizeof(Line));
300 + term.hist = xrealloc(term.hist, histsize * sizeof(Line));
301 term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
302 term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
304 + for(i = 0; i < histsize; i++) {
305 + term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph));
306 + for(j = mincol; j < col; j++) {
307 + term.hist[i][j] = term.c.attr;
308 + term.hist[i][j].u = ' ';
312 /* resize each row to new width, zero-pad if needed */
313 for(i = 0; i < minrow; i++) {
314 term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
315 @@ -3664,11 +3732,11 @@ drawregion(int x1, int y1, int x2, int y2) {
318 specs = term.specbuf;
319 - numspecs = xmakeglyphfontspecs(specs, &term.line[y][x1], x2 - x1, x1, y);
320 + numspecs = xmakeglyphfontspecs(specs, &TLINE(y)[x1], x2 - x1, x1, y);
323 for(x = x1; x < x2 && i < numspecs; x++) {
324 - new = term.line[y][x];
326 if(new.mode == ATTR_WDUMMY)
328 if(ena_sel && selected(x, y))
329 @@ -3688,7 +3756,8 @@ drawregion(int x1, int y1, int x2, int y2) {
331 xdrawglyphfontspecs(specs, base, i, ox, y);