--
--static uint64_t
--sb_view_start(void)
--{
-- return sb.base + sb.len - view_offset;
++}
++
++static Line
++sb_get(int idx)
++{
++ /* idx is logical: 0..sb.len-1 */
++ if (idx < 0 || idx >= sb.len)
++ return NULL;
++ return sb.buf[sb_phys_index(idx)];
++}
++
++static void
++sb_clear(void)
++{
++ int i;
++ int p;
++
++ if (!sb.buf)
++ return;
++
++ for (i = 0; i < sb.len; i++) {
++ p = sb_phys_index(i);
++ if (sb.buf[p]) {
++ free(sb.buf[p]);
++ sb.buf[p] = NULL;
++ }
++ }
++
++ sb.len = 0;
++ sb.head = 0;
++ sb.base = 0;
+ sb.view_offset = 0;
+ sb.max_width = 0;
- }
-
++}
++
+/*
+ * Reflows the scrollback buffer to fit a new terminal width.
+ *
_at_@ -169,11 +216,9 @@ index dc30082..4d79f20 100644
+ * Note: During reflow we reset sb to match the rebuilt buffer
+ * (head, base and len might change).
+ */
- static void
--sb_view_changed(void)
++static void
+sb_resize(int col)
- {
-- if (!term.dirty || term.row <= 0)
++{
+ Line *new_buf;
+ int i, j;
+ int new_len, logical_cap, logical_len, is_wrapped, cursor;
_at_@ -187,8 +232,7 @@ index dc30082..4d79f20 100644
+ new_len = 0;
+
+ if (sb.len == 0)
- return;
-- tfulldirt();
++ return;
+
+ new_buf = xmalloc(sizeof(Line) * sb.cap);
+ for (i = 0; i < sb.cap; i++)
_at_@ -283,59 +327,60 @@ index dc30082..4d79f20 100644
+ sb.base = new_base;
+ sb.view_offset = 0;
+ sb.max_width = new_max_width;
- }
-
- static void
--selscrollback(int delta)
++}
++
++static void
+sb_pop_screen(int loaded, int new_cols)
- {
-- if (delta == 0)
-- return;
++{
+ int i, p;
+ int start_logical;
+ Line line;
-
-- if (sel.ob.x == -1)
-- return;
++
+ loaded = MIN(loaded, sb.len);
+ start_logical = sb.len - loaded;
+ new_cols = MIN(new_cols, term.col);
+ for (i = 0; i < loaded; i++) {
+ p = sb_phys_index(start_logical + i);
+ line = sb.buf[p];
-
-- if (sel.alt != IS_SET(MODE_ALTSCREEN))
-- return;
++
+ memcpy(term.line[i], line, new_cols * sizeof(Glyph));
-
-- sel.nb.y += delta;
-- sel.ne.y += delta;
-- sel.ob.y += delta;
-- sel.oe.y += delta;
++
+ free(line);
+ sb.buf[p] = NULL;
+ }
-
-- /* the selection is in window coordinates: drop it once it leaves */
-- if (sel.nb.y < 0 || sel.ne.y >= term.row)
-- selclear();
++
+ sb.len -= loaded;
+}
-
-- sb_view_changed();
++
+static void
+sb_view_changed(void)
+{
+ if (!term.dirty || term.row <= 0)
+ return;
+ tfulldirt();
- }
-
- static Line
-_at_@ -394,14 +541,16 @@ emptyline(void)
- return empty;
- }
-
++}
++
++static Line
++emptyline(void)
++{
++ static Line empty;
++ static int empty_cols;
++ int i = 0;
++
++ if (empty_cols != term.col) {
++ free(empty);
++ empty = xmalloc(term.col * sizeof(Glyph));
++ empty_cols = term.col;
++ }
++
++ for (i = 0; i < term.col; i++) {
++ empty[i] = term.c.attr;
++ empty[i].u = ' ';
++ empty[i].mode = 0;
++ }
++ return empty;
++}
++
+/*
+ * Logical rows address history and screen as one continuous tab:
+ * 0..sb.len-1 are scrollback lines (0 = oldest) and sb.len + y is
_at_@ -343,29 +388,23 @@ index dc30082..4d79f20 100644
+ * scrollback; only evicting the oldest line shifts everything by one.
+ * The selection lives in this tab, so it survives scrolling.
+ */
- static Line
--getlineforrender(int y)
++static Line
+logicalline(int v)
- {
-- int start, v;
--
-- start = sb.len - view_offset; /* can be negative */
-- v = start + y;
--
- if (v < 0)
- return emptyline();
-
-_at_@ -410,17 +559,35 @@ getlineforrender(int y)
-
- /* past scrollback -> into current screen */
- v -= sb.len;
-- if (v >= 0 && v < term.row)
++{
++ if (v < 0)
++ return emptyline();
++
++ if (v < sb.len)
++ return sb_get(v);
++
++ /* past scrollback -> into current screen */
++ v -= sb.len;
+ if (v < term.row)
- return term.line[v];
-
- return emptyline();
- }
-
++ return term.line[v];
++
++ return emptyline();
++}
++
+/* first logical row shown at the top of the window */
+static int
+viewtop(void)
_at_@ -382,17 +421,25 @@ index dc30082..4d79f20 100644
+ return logicalline(viewtop() + y);
+}
+
- static void
- sb_reset_on_clear(void)
- {
- sb_clear();
- sb_view_changed();
++static void
++sb_reset_on_clear(void)
++{
++ sb_clear();
++ sb_view_changed();
+ if (sel.ob.x != -1 && term.row > 0)
+ selclear();
- }
-
- int
-_at_@ -601,39 +768,66 @@ selinit(void)
++}
++
++int
++tisaltscreen(void)
++{
++ return IS_SET(MODE_ALTSCREEN);
++}
++
+ ssize_t
+ xwrite(int fd, const char *s, size_t len)
+ {
+_at_@ -404,20 +768,68 @@ selinit(void)
sel.ob.x = -1;
}
_at_@ -415,51 +462,39 @@ index dc30082..4d79f20 100644
}
+/* range of logical rows a selection can cover */
- static int
--tlinelen_render(int y)
++static int
+selminrow(void)
- {
-- int i;
-- Line line;
++{
+ /* the alternate screen has no history */
+ return sel.alt ? sb.len : 0;
+}
-
-- if (term.col <= 0)
-- return 0;
++
+static int
+selmaxrow(void)
+{
+ return sb.len + term.row - 1;
+}
-
-- i = term.col;
-- line = getlineforrender(y);
++
+/* mark the visible part of logical rows a..b dirty */
+static void
+seldirt(int a, int b)
+{
+ int top = viewtop();
-
-- if (line[i - 1].mode & ATTR_WRAP)
-- return i;
++
+ a -= top;
+ b -= top;
+ if (b < 0 || a >= term.row)
+ return;
+ tsetdirt(MAX(a, 0), MIN(b, term.row - 1));
+}
-
-- while (i > 0 && line[i - 1].u == ' ')
-- --i;
++
+/* the oldest scrollback line was dropped: shift the selection with it */
+static void
+selevict(void)
+{
+ if (sel.ob.x == -1 || sel.alt)
+ return;
-
-- return i;
++
+ sel.ob.y--;
+ sel.oe.y--;
+ sel.nb.y--;
_at_@ -475,10 +510,12 @@ index dc30082..4d79f20 100644
+ sel.ob.x = sel.ob.y = 0;
+ if (sel.oe.y < 0)
+ sel.oe.x = sel.oe.y = 0;
- }
-
++}
++
void
-_at_@ -645,12 +839,12 @@ selstart(int col, int row, int snap)
+ selstart(int col, int row, int snap)
+ {
+_at_@ -427,12 +839,12 @@ selstart(int col, int row, int snap)
sel.alt = IS_SET(MODE_ALTSCREEN);
sel.snap = snap;
sel.oe.x = sel.ob.x = col;
_at_@ -493,7 +530,7 @@ index dc30082..4d79f20 100644
}
void
-_at_@ -672,16 +866,48 @@ selextend(int col, int row, int type, int done)
+_at_@ -454,16 +866,48 @@ selextend(int col, int row, int type, int done)
oldtype = sel.type;
sel.oe.x = col;
_at_@ -544,15 +581,15 @@ index dc30082..4d79f20 100644
void
selnormalize(void)
{
-_at_@ -703,15 +929,16 @@ selnormalize(void)
+_at_@ -485,15 +929,16 @@ selnormalize(void)
/* expand selection over line breaks */
if (sel.type == SEL_RECTANGULAR)
return;
-- i = tlinelen_render(sel.nb.y);
+- i = tlinelen(sel.nb.y);
+ i = tlinelen(logicalline(sel.nb.y));
if (i < sel.nb.x)
sel.nb.x = i;
-- if (tlinelen_render(sel.ne.y) <= sel.ne.x)
+- if (tlinelen(sel.ne.y) <= sel.ne.x)
+ if (tlinelen(logicalline(sel.ne.y)) <= sel.ne.x)
sel.ne.x = term.col - 1;
}
_at_@ -565,7 +602,7 @@ index dc30082..4d79f20 100644
{
if (sel.mode == SEL_EMPTY || sel.ob.x == -1 ||
sel.alt != IS_SET(MODE_ALTSCREEN))
-_at_@ -726,11 +953,19 @@ selected(int x, int y)
+_at_@ -508,12 +953,21 @@ selected(int x, int y)
&& (y != sel.ne.y || x <= sel.ne.x);
}
_at_@ -583,18 +620,20 @@ index dc30082..4d79f20 100644
int delim, prevdelim;
+ int miny = selminrow(), maxy = selmaxrow();
const Glyph *gp, *prevgp;
- Line line;
++ Line line;
-_at_@ -740,7 +975,7 @@ selsnap(int *x, int *y, int direction)
+ hub (sel.snap) {
+ case SNAP_WORD:
+_at_@ -521,7 +975,7 @@ selsnap(int *x, int *y, int direction)
* Snap around if the word wraps around at the end or
* beginning of a line.
*/
-- prevgp = &getlineforrender(*y)[*x];
+- prevgp = &term.line[*y][*x];
+ prevgp = &logicalline(*y)[*x];
prevdelim = ISDELIM(prevgp->u);
for (;;) {
newx = *x + direction;
-_at_@ -748,22 +983,23 @@ selsnap(int *x, int *y, int direction)
+_at_@ -529,21 +983,23 @@ selsnap(int *x, int *y, int direction)
if (!BETWEEN(newx, 0, term.col - 1)) {
newy += direction;
newx = (newx + term.col) % term.col;
_at_@ -606,28 +645,28 @@ index dc30082..4d79f20 100644
yt = *y, xt = *x;
else
yt = newy, xt = newx;
-- line = getlineforrender(yt);
+- if (!(term.line[yt][xt].mode & ATTR_WRAP))
+ line = logicalline(yt);
- if (!(line[xt].mode & ATTR_WRAP))
++ if (!(line[xt].mode & ATTR_WRAP))
break;
}
-- if (newx >= tlinelen_render(newy))
+- if (newx >= tlinelen(newy))
+ line = logicalline(newy);
+ if (newx >= tlinelen(line))
break;
-- gp = &getlineforrender(newy)[newx];
+- gp = &term.line[newy][newx];
+ gp = &line[newx];
delim = ISDELIM(gp->u);
if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
|| (delim && gp->u != prevgp->u)))
-_at_@ -783,15 +1019,15 @@ selsnap(int *x, int *y, int direction)
+_at_@ -563,15 +1019,15 @@ selsnap(int *x, int *y, int direction)
*/
*x = (direction < 0) ? 0 : term.col - 1;
if (direction < 0) {
- for (; *y > 0; *y += direction) {
-- if (!(getlineforrender(*y-1)[term.col-1].mode
+- if (!(term.line[*y-1][term.col-1].mode
+ for (; *y > miny; *y += direction) {
+ if (!(logicalline(*y-1)[term.col-1].mode
& ATTR_WRAP)) {
_at_@ -636,13 +675,13 @@ index dc30082..4d79f20 100644
}
} else if (direction > 0) {
- for (; *y < term.row-1; *y += direction) {
-- if (!(getlineforrender(*y)[term.col-1].mode
+- if (!(term.line[*y][term.col-1].mode
+ for (; *y < maxy; *y += direction) {
+ if (!(logicalline(*y)[term.col-1].mode
& ATTR_WRAP)) {
break;
}
-_at_@ -805,8 +1041,9 @@ char *
+_at_@ -585,8 +1041,9 @@ char *
getsel(void)
{
char *str, *ptr;
_at_@ -653,12 +692,11 @@ index dc30082..4d79f20 100644
if (sel.ob.x == -1)
return NULL;
-_at_@ -816,8 +1053,10 @@ getsel(void)
+_at_@ -596,29 +1053,33 @@ getsel(void)
/* append every set & selected glyph to the selection */
for (y = sel.nb.y; y <= sel.ne.y; y++) {
-- Line line = getlineforrender(y);
-- if ((linelen = tlinelen_render(y)) == 0) {
+- if ((linelen = tlinelen(y)) == 0) {
+ line = logicalline(y);
+ linelen = tlinelen(line);
+
_at_@ -666,11 +704,17 @@ index dc30082..4d79f20 100644
*ptr++ = '
';
continue;
}
-_at_@ -829,17 +1068,18 @@ getsel(void)
- gp = &line[sel.nb.y == y ? sel.nb.x : 0];
+
+ if (sel.type == SEL_RECTANGULAR) {
+- gp = &term.line[y][sel.nb.x];
++ gp = &line[sel.nb.x];
+ lastx = sel.ne.x;
+ } else {
+- gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
++ gp = &line[sel.nb.y == y ? sel.nb.x : 0];
lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
}
-- last = &line[MIN(lastx, linelen-1)];
+- last = &term.line[y][MIN(lastx, linelen-1)];
- while (last >= gp && last->u == ' ')
+ end_idx = MIN(lastx, linelen-1);
+ is_wrapped = (line[end_idx].mode & ATTR_WRAP) != 0;
_at_@ -689,7 +733,7 @@ index dc30082..4d79f20 100644
/*
* Copy and pasting of line endings is inconsistent
* in the inconsistent terminal and GUI world.
-_at_@ -849,8 +1089,13 @@ getsel(void)
+_at_@ -628,8 +1089,13 @@ getsel(void)
* st.
* FIXME: Fix the thin client world.
*/
_at_@ -704,7 +748,7 @@ index dc30082..4d79f20 100644
*ptr++ = '
';
}
*ptr = 0;
-_at_@ -864,7 +1109,7 @@ selclear(void)
+_at_@ -643,7 +1109,7 @@ selclear(void)
return;
sel.mode = SEL_IDLE;
sel.ob.x = -1;
_at_@ -713,26 +757,19 @@ index dc30082..4d79f20 100644
}
void
-_at_@ -1069,14 +1314,13 @@ ttyread(void)
- void
- ttywrite(const char *s, size_t n, int may_echo)
+_at_@ -850,6 +1316,11 @@ ttywrite(const char *s, size_t n, int may_echo)
{
-- if (view_offset > 0) {
-- selclear();
-- view_offset = 0;
-+ const char *next;
-+
+ const char *next;
+
+ if (sb.view_offset > 0) {
+ sb.view_offset = 0;
- sb_view_changed();
- }
-
-- const char *next;
--
++ sb_view_changed();
++ }
++
if (may_echo && IS_SET(MODE_ECHO))
twrite(s, n, 1);
-_at_@ -1197,9 +1441,8 @@ tsetdirt(int top, int bot)
+_at_@ -970,9 +1441,8 @@ tsetdirt(int top, int bot)
{
int i;
_at_@ -743,7 +780,7 @@ index dc30082..4d79f20 100644
LIMIT(top, 0, term.row-1);
LIMIT(bot, 0, term.row-1);
-_at_@ -1265,12 +1508,16 @@ treset(void)
+_at_@ -1038,15 +1508,21 @@ treset(void)
for (i = 0; i < 2; i++) {
tmoveto(0, 0);
tcursor(CURSOR_SAVE);
_at_@ -752,7 +789,7 @@ index dc30082..4d79f20 100644
+ tclearregion(0, 0, term.col-1, term.row-1);
tswapscreen();
}
- sb_clear();
++ sb_clear();
+ if (sel.ob.x != -1 && term.row > 0)
+ selclear();
}
_at_@ -761,22 +798,22 @@ index dc30082..4d79f20 100644
void
tnew(int col, int row)
{
-_at_@ -1315,35 +1562,53 @@ void
+ term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };
++ sb_init(scrollback_lines);
+ tresize(col, row);
+ treset();
+ }
+_at_@ -1086,10 +1562,54 @@ void
tscrollup(int orig, int n)
{
int i;
-- uint64_t newstart;
-- uint64_t oldstart;
--
+ int attop;
+ int pushed = 0;
Line temp;
-- oldstart = sb_view_start();
LIMIT(n, 0, term.bot-orig+1);
- if (!IS_SET(MODE_ALTSCREEN) && orig == term.top) {
-- int attop;
++ if (!IS_SET(MODE_ALTSCREEN) && orig == term.top) {
+ /*
+ * Pushed lines keep their logical rows, but rows outside
+ * of a partial scroll region move along with the screen.
_at_@ -784,34 +821,25 @@ index dc30082..4d79f20 100644
+ if ((term.top > 0 || term.bot < term.row-1) &&
+ sel.ob.x != -1 && !sel.alt && sel.ne.y >= sb.len)
+ selclear();
-
- /* At top of history only if history exists */
-- attop = (sb.len != 0 && view_offset == sb.len);
++
++ /* At top of history only if history exists */
+ attop = (sb.len != 0 && sb.view_offset == sb.len);
-
-- if (view_offset > 0 && !attop)
-- view_offset += n;
++
+ if (sb.view_offset > 0 && !attop)
+ sb.view_offset += n;
-
- for (i = 0; i < n; i++)
- sb_push(term.line[orig + i]);
++
++ for (i = 0; i < n; i++)
++ sb_push(term.line[orig + i]);
+ pushed = sb.cap > 0;
-
++
+ /* if at the top, keep me there */
- if (attop)
-- view_offset = sb.len;
-- else if (view_offset > sb.len)
-- view_offset = sb.len;
++ if (attop)
+ sb.view_offset = sb.len;
+ /* otherwise clamp me */
+ else if (sb.view_offset > sb.len)
+ sb.view_offset = sb.len;
- }
-
-- newstart = sb_view_start();
-- if (view_offset > 0)
-- selscrollback(oldstart - newstart);
++ }
++
+ if (pushed) {
+ /*
+ * The lines went to history, so the selection stays valid.
_at_@ -826,15 +854,14 @@ index dc30082..4d79f20 100644
+ tclearregion(0, term.bot-n+1, term.col-1, term.bot);
+ return;
+ }
-
++
tclearregion(0, orig, term.col-1, orig+n-1);
tsetdirt(orig+n, term.bot);
-_at_@ -1360,18 +1625,23 @@ tscrollup(int orig, int n)
+
+_at_@ -1105,16 +1625,23 @@ tscrollup(int orig, int n)
void
selscroll(int orig, int n)
{
-- if (view_offset != 0)
-- return;
+ int top, bot;
+
if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN))
_at_@ -859,7 +886,7 @@ index dc30082..4d79f20 100644
selclear();
} else {
selnormalize();
-_at_@ -1507,7 +1777,7 @@ tclearregion(int x1, int y1, int x2, int y2)
+_at_@ -1250,7 +1777,7 @@ tclearregion(int x1, int y1, int x2, int y2)
term.dirty[y] = 1;
for (x = x1; x <= x2; x++) {
gp = &term.line[y][x];
_at_@ -868,7 +895,20 @@ index dc30082..4d79f20 100644
selclear();
gp->fg = term.c.attr.fg;
gp->bg = term.c.attr.bg;
-_at_@ -2379,7 +2649,7 @@ tdumpline(int n)
+_at_@ -1727,6 +2254,12 @@ csihandle(void)
+ break;
+ case 2: /* all */
+ tclearregion(0, 0, term.col-1, term.row-1);
++ if (!IS_SET(MODE_ALTSCREEN))
++ sb_reset_on_clear();
++ break;
++ case 3:
++ if (!IS_SET(MODE_ALTSCREEN))
++ sb_reset_on_clear();
+ break;
+ default:
+ goto unknown;
+_at_@ -2116,7 +2649,7 @@ tdumpline(int n)
const Glyph *bp, *end;
bp = &term.line[n][0];
_at_@ -877,25 +917,37 @@ index dc30082..4d79f20 100644
if (bp != end || bp->u != ' ') {
for ( ; bp <= end; ++bp)
tprinter(buf, utf8encode(bp->u, buf));
-_at_@ -2439,15 +2709,8 @@ tdeftran(char ascii)
- static void
- kscroll(const Arg *arg)
- {
-- uint64_t oldstart;
-- uint64_t newstart;
--
-- oldstart = sb_view_start();
-- view_offset += arg->i;
-- LIMIT(view_offset, 0, sb.len);
-- newstart = sb_view_start();
--
-- selscrollback(oldstart - newstart);
-+ sb.view_offset += arg->i;
-+ LIMIT(sb.view_offset, 0, sb.len);
- redraw();
+_at_@ -2173,6 +2706,29 @@ tdeftran(char ascii)
+ }
}
-_at_@ -2793,7 +3056,7 @@ check_control_code:
++static void
++kscroll(const Arg *arg)
++{
++ sb.view_offset += arg->i;
++ LIMIT(sb.view_offset, 0, sb.len);
++ redraw();
++}
++
++void
++kscrolldown(const Arg *arg)
++{
++ Arg a;
++
++ a.i = -arg->i;
++ kscroll(&a);
++}
++
++void
++kscrollup(const Arg *arg)
++{
++ kscroll(arg);
++}
++
+ void
+ tdectest(char c)
+ {
+_at_@ -2500,7 +3056,7 @@ check_control_code:
*/
return;
}
_at_@ -904,7 +956,7 @@ index dc30082..4d79f20 100644
selclear();
gp = &term.line[term.c.y][term.c.x];
-_at_@ -2872,86 +3135,146 @@ twrite(const char *buf, int buflen, int show_ctrl)
+_at_@ -2579,83 +3135,146 @@ twrite(const char *buf, int buflen, int show_ctrl)
void
tresize(int col, int row)
{
_at_@ -915,9 +967,6 @@ index dc30082..4d79f20 100644
- int mincol = MIN(col, term.col);
- int *bp;
- TCursor c;
--
-- sb_clear();
-- sb_view_changed();
+ int old_row = term.row;
+ int old_col = term.col;
+ int save_end = 0; /* Track effective pushed height */
_at_@ -1112,29 +1161,52 @@ index dc30082..4d79f20 100644
}
void
-_at_@ -2970,7 +3293,7 @@ drawregion(int x1, int y1, int x2, int y2)
+_at_@ -2669,12 +3288,13 @@ drawregion(int x1, int y1, int x2, int y2)
+ {
+ int y;
+
++ Line line;
+ for (y = y1; y < y2; y++) {
if (!term.dirty[y])
continue;
+-
term.dirty[y] = 0;
-- line = getlineforrender(y);
+- xdrawline(term.line[y], x1, y, x2);
+ line = renderline(y);
- xdrawline(line, x1, y, x2);
++ xdrawline(line, x1, y, x2);
}
}
-_at_@ -2992,7 +3315,7 @@ draw(void)
+
+_at_@ -2695,10 +3315,12 @@ draw(void)
cx--;
drawregion(0, 0, term.col, term.row);
-- if (view_offset == 0) {
+- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
+- term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
+- term.ocx = cx;
+- term.ocy = term.c.y;
+ if (sb.view_offset == 0) {
- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
- term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
- term.ocx = cx;
++ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
++ term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
++ term.ocx = cx;
++ term.ocy = term.c.y;
++ }
+ xfinishdraw();
+ if (ocx != term.ocx || ocy != term.ocy)
+ xximspot(term.ocx, term.ocy);
diff --dropbox a/st.h b/st.h
-index 151d0c6..66f1436 100644
+index fd3b0d8..66f1436 100644
--- a/st.h
+++ b/st.h
-_at_@ -103,6 +103,7 @@ void selclear(void);
+_at_@ -86,6 +86,7 @@ void printsel(const Arg *);
+ void sendbreak(const Arg *);
+ void toggleprinter(const Arg *);
+
++int tisaltscreen(void);
+ int tattrset(int);
+ void tnew(int, int);
+ void tresize(int, int);
+_at_@ -102,6 +103,7 @@ void selclear(void);
void selinit(void);
void selstart(int, int, int);
void selextend(int, int, int, int);
_at_@ -1142,8 +1214,23 @@ index 151d0c6..66f1436 100644
int selected(int, int);
char *getsel(void);
+_at_@ -111,6 +113,9 @@ void *xmalloc(size_t);
+ void *xrealloc(void *, size_t);
+ char *xstrdup(const char *);
+
++void kscrollup(const Arg *arg);
++void kscrolldown(const Arg *arg);
++
+ /* config.h globals */
+ extern char *utmp;
+ extern char *scroll;
+_at_@ -124,3 +129,4 @@ extern unsigned int spacetabs;
+ extern unsigned int defaultfg;
+ extern unsigned int defaultbg;
+ extern unsigned int defaultcs;
++extern unsigned int scrollback_lines;
diff --dropbox a/x.c b/x.c
-index dfe416a..8091e6f 100644
+index 76f572d..8091e6f 100644
--- a/x.c
+++ b/x.c
_at_@ -181,6 +181,7 @@ static void selnotify(XEvent *);
_at_@ -1186,17 +1273,34 @@ index dfe416a..8091e6f 100644
if (done)
setsel(getsel(), e->xbutton.time);
}
-_at_@ -485,6 +490,9 @@ bpress(XEvent *e)
- } else {
- kscrolldown(&a);
- }
+_at_@ -472,6 +477,26 @@ bpress(XEvent *e)
+ struct timespec now;
+ int snap;
+
++ if (btn == Button4 || btn == Button5) {
++ Arg a;
++ if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
++ mousereport(e);
++ return;
++ }
++ if (!tisaltscreen()) {
++ a.i = 1;
++ if (btn == Button4) {
++ kscrollup(&a);
++ } else {
++ kscrolldown(&a);
++ }
+ /* keep a selection being dragged under the pointer */
+ if (buttons & 1)
+ mousesel(e, 0);
- }
- return;
- }
-_at_@ -501,6 +509,11 @@ bpress(XEvent *e)
++ }
++ return;
++ }
++
+ if (1 <= btn && btn <= 11)
+ buttons |= 1 << (btn-1);
+
+_at_@ -484,6 +509,11 @@ bpress(XEvent *e)
return;
if (btn == Button1) {
Received on Mon Oct 19 2026 - 03:03:19 CEST
This archive was generated by hypermail 2.3.0 : Mon Oct 19 2026 - 03:12:54 CEST