Terminal scrollback with ring buffer
This patch adds a ring buffer for scrollback to the terminal. The advantage of using a ring buffer is that the common case, scrolling with no static screen content, can be achieved very efficiently by incrementing and decrementing the starting line (modulo buffer size). The scrollback buffer is limited to HISTSIZE lines in order to bound memory usage. As the lines are allocated on demand, it is possible to implement unlimited scrollback with few changes. If the terminal is reset, the scroll back buffer is reset, too. (apply st-scrollback-ringbuffer-0.8.5.diff on top of colorscheme)
This commit is contained in:
parent
01b6343666
commit
07b35d356c
@ -253,6 +253,8 @@ static Shortcut shortcuts[] = {
|
|||||||
{ MODKEY, XK_9, selectscheme, {.i = 8} },
|
{ MODKEY, XK_9, selectscheme, {.i = 8} },
|
||||||
{ MODKEY, XK_0, nextscheme, {.i = +1} },
|
{ MODKEY, XK_0, nextscheme, {.i = +1} },
|
||||||
{ MODKEY|ControlMask, XK_0, nextscheme, {.i = -1} },
|
{ MODKEY|ControlMask, XK_0, nextscheme, {.i = -1} },
|
||||||
|
{ ShiftMask, XK_Page_Up, kscrollup, {.i = -1} },
|
||||||
|
{ ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} },
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
371
st.c
371
st.c
@ -43,6 +43,10 @@
|
|||||||
#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
|
#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
|
||||||
#define ISDELIM(u) (u && wcschr(worddelimiters, u))
|
#define ISDELIM(u) (u && wcschr(worddelimiters, u))
|
||||||
|
|
||||||
|
#define TSCREEN term.screen[IS_SET(MODE_ALTSCREEN)]
|
||||||
|
#define TLINEOFFSET(y) (((y) + TSCREEN.cur - TSCREEN.off + TSCREEN.size) % TSCREEN.size)
|
||||||
|
#define TLINE(y) (TSCREEN.buffer[TLINEOFFSET(y)])
|
||||||
|
|
||||||
enum term_mode {
|
enum term_mode {
|
||||||
MODE_WRAP = 1 << 0,
|
MODE_WRAP = 1 << 0,
|
||||||
MODE_INSERT = 1 << 1,
|
MODE_INSERT = 1 << 1,
|
||||||
@ -109,12 +113,21 @@ typedef struct {
|
|||||||
int alt;
|
int alt;
|
||||||
} Selection;
|
} Selection;
|
||||||
|
|
||||||
|
/* Screen lines */
|
||||||
|
typedef struct {
|
||||||
|
Line* buffer; /* ring buffer */
|
||||||
|
int size; /* size of buffer */
|
||||||
|
int cur; /* start of active screen */
|
||||||
|
int off; /* scrollback line offset */
|
||||||
|
TCursor sc; /* saved cursor */
|
||||||
|
} LineBuffer;
|
||||||
|
|
||||||
/* Internal representation of the screen */
|
/* Internal representation of the screen */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int row; /* nb row */
|
int row; /* nb row */
|
||||||
int col; /* nb col */
|
int col; /* nb col */
|
||||||
Line *line; /* screen */
|
LineBuffer screen[2]; /* screen and alternate screen */
|
||||||
Line *alt; /* alternate screen */
|
int linelen; /* allocated line length */
|
||||||
int *dirty; /* dirtyness of lines */
|
int *dirty; /* dirtyness of lines */
|
||||||
TCursor c; /* cursor */
|
TCursor c; /* cursor */
|
||||||
int ocx; /* old cursor col */
|
int ocx; /* old cursor col */
|
||||||
@ -203,6 +216,8 @@ static void tdeftran(char);
|
|||||||
static void tstrsequence(uchar);
|
static void tstrsequence(uchar);
|
||||||
|
|
||||||
static void drawregion(int, int, int, int);
|
static void drawregion(int, int, int, int);
|
||||||
|
static void clearline(Line, Glyph, int, int);
|
||||||
|
static Line ensureline(Line);
|
||||||
|
|
||||||
static void selnormalize(void);
|
static void selnormalize(void);
|
||||||
static void selscroll(int, int);
|
static void selscroll(int, int);
|
||||||
@ -408,11 +423,12 @@ int
|
|||||||
tlinelen(int y)
|
tlinelen(int y)
|
||||||
{
|
{
|
||||||
int i = term.col;
|
int i = term.col;
|
||||||
|
Line line = TLINE(y);
|
||||||
|
|
||||||
if (term.line[y][i - 1].mode & ATTR_WRAP)
|
if (line[i - 1].mode & ATTR_WRAP)
|
||||||
return i;
|
return i;
|
||||||
|
|
||||||
while (i > 0 && term.line[y][i - 1].u == ' ')
|
while (i > 0 && line[i - 1].u == ' ')
|
||||||
--i;
|
--i;
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
@ -521,7 +537,7 @@ selsnap(int *x, int *y, int direction)
|
|||||||
* Snap around if the word wraps around at the end or
|
* Snap around if the word wraps around at the end or
|
||||||
* beginning of a line.
|
* beginning of a line.
|
||||||
*/
|
*/
|
||||||
prevgp = &term.line[*y][*x];
|
prevgp = &TLINE(*y)[*x];
|
||||||
prevdelim = ISDELIM(prevgp->u);
|
prevdelim = ISDELIM(prevgp->u);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
newx = *x + direction;
|
newx = *x + direction;
|
||||||
@ -536,14 +552,14 @@ selsnap(int *x, int *y, int direction)
|
|||||||
yt = *y, xt = *x;
|
yt = *y, xt = *x;
|
||||||
else
|
else
|
||||||
yt = newy, xt = newx;
|
yt = newy, xt = newx;
|
||||||
if (!(term.line[yt][xt].mode & ATTR_WRAP))
|
if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newx >= tlinelen(newy))
|
if (newx >= tlinelen(newy))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
gp = &term.line[newy][newx];
|
gp = &TLINE(newy)[newx];
|
||||||
delim = ISDELIM(gp->u);
|
delim = ISDELIM(gp->u);
|
||||||
if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
|
if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
|
||||||
|| (delim && gp->u != prevgp->u)))
|
|| (delim && gp->u != prevgp->u)))
|
||||||
@ -564,14 +580,14 @@ selsnap(int *x, int *y, int direction)
|
|||||||
*x = (direction < 0) ? 0 : term.col - 1;
|
*x = (direction < 0) ? 0 : term.col - 1;
|
||||||
if (direction < 0) {
|
if (direction < 0) {
|
||||||
for (; *y > 0; *y += direction) {
|
for (; *y > 0; *y += direction) {
|
||||||
if (!(term.line[*y-1][term.col-1].mode
|
if (!(TLINE(*y-1)[term.col-1].mode
|
||||||
& ATTR_WRAP)) {
|
& ATTR_WRAP)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (direction > 0) {
|
} else if (direction > 0) {
|
||||||
for (; *y < term.row-1; *y += direction) {
|
for (; *y < term.row-1; *y += direction) {
|
||||||
if (!(term.line[*y][term.col-1].mode
|
if (!(TLINE(*y)[term.col-1].mode
|
||||||
& ATTR_WRAP)) {
|
& ATTR_WRAP)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -602,13 +618,13 @@ getsel(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sel.type == SEL_RECTANGULAR) {
|
if (sel.type == SEL_RECTANGULAR) {
|
||||||
gp = &term.line[y][sel.nb.x];
|
gp = &TLINE(y)[sel.nb.x];
|
||||||
lastx = sel.ne.x;
|
lastx = sel.ne.x;
|
||||||
} else {
|
} else {
|
||||||
gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
|
gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0];
|
||||||
lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
|
lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
|
||||||
}
|
}
|
||||||
last = &term.line[y][MIN(lastx, linelen-1)];
|
last = &TLINE(y)[MIN(lastx, linelen-1)];
|
||||||
while (last >= gp && last->u == ' ')
|
while (last >= gp && last->u == ' ')
|
||||||
--last;
|
--last;
|
||||||
|
|
||||||
@ -949,12 +965,15 @@ int
|
|||||||
tattrset(int attr)
|
tattrset(int attr)
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
|
int y = TLINEOFFSET(0);
|
||||||
|
|
||||||
for (i = 0; i < term.row-1; i++) {
|
for (i = 0; i < term.row-1; i++) {
|
||||||
|
Line line = TSCREEN.buffer[y];
|
||||||
for (j = 0; j < term.col-1; j++) {
|
for (j = 0; j < term.col-1; j++) {
|
||||||
if (term.line[i][j].mode & attr)
|
if (line[j].mode & attr)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
y = (y+1) % TSCREEN.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -976,14 +995,17 @@ void
|
|||||||
tsetdirtattr(int attr)
|
tsetdirtattr(int attr)
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
|
int y = TLINEOFFSET(0);
|
||||||
|
|
||||||
for (i = 0; i < term.row-1; i++) {
|
for (i = 0; i < term.row-1; i++) {
|
||||||
|
Line line = TSCREEN.buffer[y];
|
||||||
for (j = 0; j < term.col-1; j++) {
|
for (j = 0; j < term.col-1; j++) {
|
||||||
if (term.line[i][j].mode & attr) {
|
if (line[j].mode & attr) {
|
||||||
tsetdirt(i, i);
|
tsetdirt(i, i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
y = (y+1) % TSCREEN.size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -996,27 +1018,19 @@ tfulldirt(void)
|
|||||||
void
|
void
|
||||||
tcursor(int mode)
|
tcursor(int mode)
|
||||||
{
|
{
|
||||||
static TCursor c[2];
|
|
||||||
int alt = IS_SET(MODE_ALTSCREEN);
|
|
||||||
|
|
||||||
if (mode == CURSOR_SAVE) {
|
if (mode == CURSOR_SAVE) {
|
||||||
c[alt] = term.c;
|
TSCREEN.sc = term.c;
|
||||||
} else if (mode == CURSOR_LOAD) {
|
} else if (mode == CURSOR_LOAD) {
|
||||||
term.c = c[alt];
|
term.c = TSCREEN.sc;
|
||||||
tmoveto(c[alt].x, c[alt].y);
|
tmoveto(term.c.x, term.c.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
treset(void)
|
treset(void)
|
||||||
{
|
{
|
||||||
uint i;
|
int i, j;
|
||||||
|
Glyph g = (Glyph){ .fg = defaultfg, .bg = defaultbg};
|
||||||
term.c = (TCursor){{
|
|
||||||
.mode = ATTR_NULL,
|
|
||||||
.fg = defaultfg,
|
|
||||||
.bg = defaultbg
|
|
||||||
}, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
|
|
||||||
|
|
||||||
memset(term.tabs, 0, term.col * sizeof(*term.tabs));
|
memset(term.tabs, 0, term.col * sizeof(*term.tabs));
|
||||||
for (i = tabspaces; i < term.col; i += tabspaces)
|
for (i = tabspaces; i < term.col; i += tabspaces)
|
||||||
@ -1028,17 +1042,37 @@ treset(void)
|
|||||||
term.charset = 0;
|
term.charset = 0;
|
||||||
|
|
||||||
for (i = 0; i < 2; i++) {
|
for (i = 0; i < 2; i++) {
|
||||||
tmoveto(0, 0);
|
term.screen[i].sc = (TCursor){{
|
||||||
tcursor(CURSOR_SAVE);
|
.fg = defaultfg,
|
||||||
tclearregion(0, 0, term.col-1, term.row-1);
|
.bg = defaultbg
|
||||||
tswapscreen();
|
}};
|
||||||
|
term.screen[i].cur = 0;
|
||||||
|
term.screen[i].off = 0;
|
||||||
|
for (j = 0; j < term.row; ++j) {
|
||||||
|
if (term.col != term.linelen)
|
||||||
|
term.screen[i].buffer[j] = xrealloc(term.screen[i].buffer[j], term.col * sizeof(Glyph));
|
||||||
|
clearline(term.screen[i].buffer[j], g, 0, term.col);
|
||||||
|
}
|
||||||
|
for (j = term.row; j < term.screen[i].size; ++j) {
|
||||||
|
free(term.screen[i].buffer[j]);
|
||||||
|
term.screen[i].buffer[j] = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
tcursor(CURSOR_LOAD);
|
||||||
|
term.linelen = term.col;
|
||||||
|
tfulldirt();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
tnew(int col, int row)
|
tnew(int col, int row)
|
||||||
{
|
{
|
||||||
term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };
|
int i;
|
||||||
|
term = (Term){};
|
||||||
|
term.screen[0].buffer = xmalloc(HISTSIZE * sizeof(Line));
|
||||||
|
term.screen[0].size = HISTSIZE;
|
||||||
|
term.screen[1].buffer = NULL;
|
||||||
|
for (i = 0; i < HISTSIZE; ++i) term.screen[0].buffer[i] = NULL;
|
||||||
|
|
||||||
tresize(col, row);
|
tresize(col, row);
|
||||||
treset();
|
treset();
|
||||||
}
|
}
|
||||||
@ -1046,14 +1080,42 @@ tnew(int col, int row)
|
|||||||
void
|
void
|
||||||
tswapscreen(void)
|
tswapscreen(void)
|
||||||
{
|
{
|
||||||
Line *tmp = term.line;
|
|
||||||
|
|
||||||
term.line = term.alt;
|
|
||||||
term.alt = tmp;
|
|
||||||
term.mode ^= MODE_ALTSCREEN;
|
term.mode ^= MODE_ALTSCREEN;
|
||||||
tfulldirt();
|
tfulldirt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kscrollup(const Arg *a)
|
||||||
|
{
|
||||||
|
int n = a->i;
|
||||||
|
|
||||||
|
if (IS_SET(MODE_ALTSCREEN))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (n < 0) n = (-n) * term.row;
|
||||||
|
if (n > TSCREEN.size - term.row - TSCREEN.off) n = TSCREEN.size - term.row - TSCREEN.off;
|
||||||
|
while (!TLINE(-n)) --n;
|
||||||
|
TSCREEN.off += n;
|
||||||
|
selscroll(0, n);
|
||||||
|
tfulldirt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kscrolldown(const Arg *a)
|
||||||
|
{
|
||||||
|
|
||||||
|
int n = a->i;
|
||||||
|
|
||||||
|
if (IS_SET(MODE_ALTSCREEN))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (n < 0) n = (-n) * term.row;
|
||||||
|
if (n > TSCREEN.off) n = TSCREEN.off;
|
||||||
|
TSCREEN.off -= n;
|
||||||
|
selscroll(0, -n);
|
||||||
|
tfulldirt();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
tscrolldown(int orig, int n)
|
tscrolldown(int orig, int n)
|
||||||
{
|
{
|
||||||
@ -1062,15 +1124,29 @@ tscrolldown(int orig, int n)
|
|||||||
|
|
||||||
LIMIT(n, 0, term.bot-orig+1);
|
LIMIT(n, 0, term.bot-orig+1);
|
||||||
|
|
||||||
tsetdirt(orig, term.bot-n);
|
/* Ensure that lines are allocated */
|
||||||
tclearregion(0, term.bot-n+1, term.col-1, term.bot);
|
for (i = -n; i < 0; i++) {
|
||||||
|
TLINE(i) = ensureline(TLINE(i));
|
||||||
for (i = term.bot; i >= orig+n; i--) {
|
|
||||||
temp = term.line[i];
|
|
||||||
term.line[i] = term.line[i-n];
|
|
||||||
term.line[i-n] = temp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Shift non-scrolling areas in ring buffer */
|
||||||
|
for (i = term.bot+1; i < term.row; i++) {
|
||||||
|
temp = TLINE(i);
|
||||||
|
TLINE(i) = TLINE(i-n);
|
||||||
|
TLINE(i-n) = temp;
|
||||||
|
}
|
||||||
|
for (i = 0; i < orig; i++) {
|
||||||
|
temp = TLINE(i);
|
||||||
|
TLINE(i) = TLINE(i-n);
|
||||||
|
TLINE(i-n) = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scroll buffer */
|
||||||
|
TSCREEN.cur = (TSCREEN.cur + TSCREEN.size - n) % TSCREEN.size;
|
||||||
|
/* Clear lines that have entered the view */
|
||||||
|
tclearregion(0, orig, term.linelen-1, orig+n-1);
|
||||||
|
/* Redraw portion of the screen that has scrolled */
|
||||||
|
tsetdirt(orig+n-1, term.bot);
|
||||||
selscroll(orig, n);
|
selscroll(orig, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1082,15 +1158,29 @@ tscrollup(int orig, int n)
|
|||||||
|
|
||||||
LIMIT(n, 0, term.bot-orig+1);
|
LIMIT(n, 0, term.bot-orig+1);
|
||||||
|
|
||||||
tclearregion(0, orig, term.col-1, orig+n-1);
|
/* Ensure that lines are allocated */
|
||||||
tsetdirt(orig+n, term.bot);
|
for (i = term.row; i < term.row + n; i++) {
|
||||||
|
TLINE(i) = ensureline(TLINE(i));
|
||||||
for (i = orig; i <= term.bot-n; i++) {
|
|
||||||
temp = term.line[i];
|
|
||||||
term.line[i] = term.line[i+n];
|
|
||||||
term.line[i+n] = temp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Shift non-scrolling areas in ring buffer */
|
||||||
|
for (i = orig-1; i >= 0; i--) {
|
||||||
|
temp = TLINE(i);
|
||||||
|
TLINE(i) = TLINE(i+n);
|
||||||
|
TLINE(i+n) = temp;
|
||||||
|
}
|
||||||
|
for (i = term.row-1; i >term.bot; i--) {
|
||||||
|
temp = TLINE(i);
|
||||||
|
TLINE(i) = TLINE(i+n);
|
||||||
|
TLINE(i+n) = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scroll buffer */
|
||||||
|
TSCREEN.cur = (TSCREEN.cur + n) % TSCREEN.size;
|
||||||
|
/* Clear lines that have entered the view */
|
||||||
|
tclearregion(0, term.bot-n+1, term.linelen-1, term.bot);
|
||||||
|
/* Redraw portion of the screen that has scrolled */
|
||||||
|
tsetdirt(orig, term.bot-n+1);
|
||||||
selscroll(orig, -n);
|
selscroll(orig, -n);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1194,6 +1284,7 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
|
|||||||
"⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */
|
"⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */
|
||||||
"│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */
|
"│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */
|
||||||
};
|
};
|
||||||
|
Line line = TLINE(y);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The table is proudly stolen from rxvt.
|
* The table is proudly stolen from rxvt.
|
||||||
@ -1202,25 +1293,25 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
|
|||||||
BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41])
|
BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41])
|
||||||
utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ);
|
utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ);
|
||||||
|
|
||||||
if (term.line[y][x].mode & ATTR_WIDE) {
|
if (line[x].mode & ATTR_WIDE) {
|
||||||
if (x+1 < term.col) {
|
if (x+1 < term.col) {
|
||||||
term.line[y][x+1].u = ' ';
|
line[x+1].u = ' ';
|
||||||
term.line[y][x+1].mode &= ~ATTR_WDUMMY;
|
line[x+1].mode &= ~ATTR_WDUMMY;
|
||||||
}
|
}
|
||||||
} else if (term.line[y][x].mode & ATTR_WDUMMY) {
|
} else if (line[x].mode & ATTR_WDUMMY) {
|
||||||
term.line[y][x-1].u = ' ';
|
line[x-1].u = ' ';
|
||||||
term.line[y][x-1].mode &= ~ATTR_WIDE;
|
line[x-1].mode &= ~ATTR_WIDE;
|
||||||
}
|
}
|
||||||
|
|
||||||
term.dirty[y] = 1;
|
term.dirty[y] = 1;
|
||||||
term.line[y][x] = *attr;
|
line[x] = *attr;
|
||||||
term.line[y][x].u = u;
|
line[x].u = u;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
tclearregion(int x1, int y1, int x2, int y2)
|
tclearregion(int x1, int y1, int x2, int y2)
|
||||||
{
|
{
|
||||||
int x, y, temp;
|
int x, y, L, S, temp;
|
||||||
Glyph *gp;
|
Glyph *gp;
|
||||||
|
|
||||||
if (x1 > x2)
|
if (x1 > x2)
|
||||||
@ -1228,15 +1319,16 @@ tclearregion(int x1, int y1, int x2, int y2)
|
|||||||
if (y1 > y2)
|
if (y1 > y2)
|
||||||
temp = y1, y1 = y2, y2 = temp;
|
temp = y1, y1 = y2, y2 = temp;
|
||||||
|
|
||||||
LIMIT(x1, 0, term.col-1);
|
LIMIT(x1, 0, term.linelen-1);
|
||||||
LIMIT(x2, 0, term.col-1);
|
LIMIT(x2, 0, term.linelen-1);
|
||||||
LIMIT(y1, 0, term.row-1);
|
LIMIT(y1, 0, term.row-1);
|
||||||
LIMIT(y2, 0, term.row-1);
|
LIMIT(y2, 0, term.row-1);
|
||||||
|
|
||||||
|
L = TLINEOFFSET(y1);
|
||||||
for (y = y1; y <= y2; y++) {
|
for (y = y1; y <= y2; y++) {
|
||||||
term.dirty[y] = 1;
|
term.dirty[y] = 1;
|
||||||
for (x = x1; x <= x2; x++) {
|
for (x = x1; x <= x2; x++) {
|
||||||
gp = &term.line[y][x];
|
gp = &TSCREEN.buffer[L][x];
|
||||||
if (selected(x, y))
|
if (selected(x, y))
|
||||||
selclear();
|
selclear();
|
||||||
gp->fg = term.c.attr.fg;
|
gp->fg = term.c.attr.fg;
|
||||||
@ -1244,6 +1336,7 @@ tclearregion(int x1, int y1, int x2, int y2)
|
|||||||
gp->mode = 0;
|
gp->mode = 0;
|
||||||
gp->u = ' ';
|
gp->u = ' ';
|
||||||
}
|
}
|
||||||
|
L = (L + 1) % TSCREEN.size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1258,7 +1351,7 @@ tdeletechar(int n)
|
|||||||
dst = term.c.x;
|
dst = term.c.x;
|
||||||
src = term.c.x + n;
|
src = term.c.x + n;
|
||||||
size = term.col - src;
|
size = term.col - src;
|
||||||
line = term.line[term.c.y];
|
line = TLINE(term.c.y);
|
||||||
|
|
||||||
memmove(&line[dst], &line[src], size * sizeof(Glyph));
|
memmove(&line[dst], &line[src], size * sizeof(Glyph));
|
||||||
tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
|
tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
|
||||||
@ -1275,7 +1368,7 @@ tinsertblank(int n)
|
|||||||
dst = term.c.x + n;
|
dst = term.c.x + n;
|
||||||
src = term.c.x;
|
src = term.c.x;
|
||||||
size = term.col - dst;
|
size = term.col - dst;
|
||||||
line = term.line[term.c.y];
|
line = TLINE(term.c.y);
|
||||||
|
|
||||||
memmove(&line[dst], &line[src], size * sizeof(Glyph));
|
memmove(&line[dst], &line[src], size * sizeof(Glyph));
|
||||||
tclearregion(src, term.c.y, dst - 1, term.c.y);
|
tclearregion(src, term.c.y, dst - 1, term.c.y);
|
||||||
@ -2078,7 +2171,7 @@ tdumpline(int n)
|
|||||||
char buf[UTF_SIZ];
|
char buf[UTF_SIZ];
|
||||||
const Glyph *bp, *end;
|
const Glyph *bp, *end;
|
||||||
|
|
||||||
bp = &term.line[n][0];
|
bp = &TLINE(n)[0];
|
||||||
end = &bp[MIN(tlinelen(n), term.col) - 1];
|
end = &bp[MIN(tlinelen(n), term.col) - 1];
|
||||||
if (bp != end || bp->u != ' ') {
|
if (bp != end || bp->u != ' ') {
|
||||||
for ( ; bp <= end; ++bp)
|
for ( ; bp <= end; ++bp)
|
||||||
@ -2176,8 +2269,8 @@ tupdatebgcolor(int oldbg, int newbg)
|
|||||||
{
|
{
|
||||||
for (int y = 0; y < term.row; y++) {
|
for (int y = 0; y < term.row; y++) {
|
||||||
for (int x = 0; x < term.col; x++) {
|
for (int x = 0; x < term.col; x++) {
|
||||||
if (term.line[y][x].bg == oldbg)
|
if (TLINE(y)[x].bg == oldbg)
|
||||||
term.line[y][x].bg = newbg;
|
TLINE(y)[x].bg = newbg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2187,8 +2280,8 @@ tupdatefgcolor(int oldfg, int newfg)
|
|||||||
{
|
{
|
||||||
for (int y = 0; y < term.row; y++) {
|
for (int y = 0; y < term.row; y++) {
|
||||||
for (int x = 0; x < term.col; x++) {
|
for (int x = 0; x < term.col; x++) {
|
||||||
if (term.line[y][x].fg == oldfg)
|
if (TLINE(y)[x].fg == oldfg)
|
||||||
term.line[y][x].fg = newfg;
|
TLINE(y)[x].fg = newfg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2486,11 +2579,11 @@ check_control_code:
|
|||||||
if (selected(term.c.x, term.c.y))
|
if (selected(term.c.x, term.c.y))
|
||||||
selclear();
|
selclear();
|
||||||
|
|
||||||
gp = &term.line[term.c.y][term.c.x];
|
gp = &TLINE(term.c.y)[term.c.x];
|
||||||
if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
|
if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
|
||||||
gp->mode |= ATTR_WRAP;
|
gp->mode |= ATTR_WRAP;
|
||||||
tnewline(1);
|
tnewline(1);
|
||||||
gp = &term.line[term.c.y][term.c.x];
|
gp = &TLINE(term.c.y)[term.c.x];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) {
|
if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) {
|
||||||
@ -2500,7 +2593,7 @@ check_control_code:
|
|||||||
|
|
||||||
if (term.c.x+width > term.col) {
|
if (term.c.x+width > term.col) {
|
||||||
tnewline(1);
|
tnewline(1);
|
||||||
gp = &term.line[term.c.y][term.c.x];
|
gp = &TLINE(term.c.y)[term.c.x];
|
||||||
}
|
}
|
||||||
|
|
||||||
tsetchar(u, &term.c.attr, term.c.x, term.c.y);
|
tsetchar(u, &term.c.attr, term.c.x, term.c.y);
|
||||||
@ -2531,6 +2624,11 @@ twrite(const char *buf, int buflen, int show_ctrl)
|
|||||||
Rune u;
|
Rune u;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
|
if (TSCREEN.off) {
|
||||||
|
TSCREEN.off = 0;
|
||||||
|
tfulldirt();
|
||||||
|
}
|
||||||
|
|
||||||
for (n = 0; n < buflen; n += charsize) {
|
for (n = 0; n < buflen; n += charsize) {
|
||||||
if (IS_SET(MODE_UTF8)) {
|
if (IS_SET(MODE_UTF8)) {
|
||||||
/* process a complete utf8 char */
|
/* process a complete utf8 char */
|
||||||
@ -2557,56 +2655,85 @@ twrite(const char *buf, int buflen, int show_ctrl)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
tresize(int col, int row)
|
clearline(Line line, Glyph g, int x, int xend)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
g.mode = 0;
|
||||||
|
g.u = ' ';
|
||||||
|
for (i = x; i < xend; ++i) {
|
||||||
|
line[i] = g;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Line
|
||||||
|
ensureline(Line line)
|
||||||
|
{
|
||||||
|
if (!line) {
|
||||||
|
line = xmalloc(term.linelen * sizeof(Glyph));
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tresize(int col, int row)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
int minrow = MIN(row, term.row);
|
int minrow = MIN(row, term.row);
|
||||||
int mincol = MIN(col, term.col);
|
int mincol = MIN(col, term.col);
|
||||||
|
int linelen = MAX(col, term.linelen);
|
||||||
int *bp;
|
int *bp;
|
||||||
TCursor c;
|
|
||||||
|
|
||||||
if (col < 1 || row < 1) {
|
if (col < 1 || row < 1 || row > HISTSIZE) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"tresize: error resizing to %dx%d\n", col, row);
|
"tresize: error resizing to %dx%d\n", col, row);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Shift buffer to keep the cursor where we expect it */
|
||||||
* slide screen to keep cursor where we expect it -
|
if (row <= term.c.y) {
|
||||||
* tscrollup would work here, but we can optimize to
|
term.screen[0].cur = (term.screen[0].cur - row + term.c.y + 1) % term.screen[0].size;
|
||||||
* memmove because we're freeing the earlier lines
|
|
||||||
*/
|
|
||||||
for (i = 0; i <= term.c.y - row; i++) {
|
|
||||||
free(term.line[i]);
|
|
||||||
free(term.alt[i]);
|
|
||||||
}
|
}
|
||||||
/* ensure that both src and dst are not NULL */
|
|
||||||
if (i > 0) {
|
/* Resize and clear line buffers as needed */
|
||||||
memmove(term.line, term.line + i, row * sizeof(Line));
|
if (linelen > term.linelen) {
|
||||||
memmove(term.alt, term.alt + i, row * sizeof(Line));
|
for (i = 0; i < term.screen[0].size; ++i) {
|
||||||
|
if (term.screen[0].buffer[i]) {
|
||||||
|
term.screen[0].buffer[i] = xrealloc(term.screen[0].buffer[i], linelen * sizeof(Glyph));
|
||||||
|
clearline(term.screen[0].buffer[i], term.c.attr, term.linelen, linelen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < minrow; ++i) {
|
||||||
|
term.screen[1].buffer[i] = xrealloc(term.screen[1].buffer[i], linelen * sizeof(Glyph));
|
||||||
|
clearline(term.screen[1].buffer[i], term.c.attr, term.linelen, linelen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (i += row; i < term.row; i++) {
|
/* Allocate all visible lines for regular line buffer */
|
||||||
free(term.line[i]);
|
for (j = term.screen[0].cur, i = 0; i < row; ++i, j = (j + 1) % term.screen[0].size)
|
||||||
free(term.alt[i]);
|
{
|
||||||
|
if (!term.screen[0].buffer[j]) {
|
||||||
|
term.screen[0].buffer[j] = xmalloc(linelen * sizeof(Glyph));
|
||||||
|
}
|
||||||
|
if (i >= term.row) {
|
||||||
|
clearline(term.screen[0].buffer[j], term.c.attr, 0, linelen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Resize alt screen */
|
||||||
|
term.screen[1].cur = 0;
|
||||||
|
term.screen[1].size = row;
|
||||||
|
for (i = row; i < term.row; ++i) {
|
||||||
|
free(term.screen[1].buffer[i]);
|
||||||
|
}
|
||||||
|
term.screen[1].buffer = xrealloc(term.screen[1].buffer, row * sizeof(Line));
|
||||||
|
for (i = term.row; i < row; ++i) {
|
||||||
|
term.screen[1].buffer[i] = xmalloc(linelen * sizeof(Glyph));
|
||||||
|
clearline(term.screen[1].buffer[i], term.c.attr, 0, linelen);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* resize to new height */
|
/* resize to new height */
|
||||||
term.line = xrealloc(term.line, row * sizeof(Line));
|
|
||||||
term.alt = xrealloc(term.alt, row * sizeof(Line));
|
|
||||||
term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
|
term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
|
||||||
term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
|
term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
|
||||||
|
|
||||||
/* resize each row to new width, zero-pad if needed */
|
/* fix tabstops */
|
||||||
for (i = 0; i < minrow; i++) {
|
|
||||||
term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
|
|
||||||
term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* allocate any new rows */
|
|
||||||
for (/* i = minrow */; i < row; i++) {
|
|
||||||
term.line[i] = xmalloc(col * sizeof(Glyph));
|
|
||||||
term.alt[i] = xmalloc(col * sizeof(Glyph));
|
|
||||||
}
|
|
||||||
if (col > term.col) {
|
if (col > term.col) {
|
||||||
bp = term.tabs + term.col;
|
bp = term.tabs + term.col;
|
||||||
|
|
||||||
@ -2616,26 +2743,16 @@ tresize(int col, int row)
|
|||||||
for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
|
for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
|
||||||
*bp = 1;
|
*bp = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update terminal size */
|
/* update terminal size */
|
||||||
term.col = col;
|
term.col = col;
|
||||||
term.row = row;
|
term.row = row;
|
||||||
|
term.linelen = linelen;
|
||||||
/* reset scrolling region */
|
/* reset scrolling region */
|
||||||
tsetscroll(0, row-1);
|
tsetscroll(0, row-1);
|
||||||
/* make use of the LIMIT in tmoveto */
|
/* make use of the LIMIT in tmoveto */
|
||||||
tmoveto(term.c.x, term.c.y);
|
tmoveto(term.c.x, term.c.y);
|
||||||
/* Clearing both screens (it makes dirty all lines) */
|
tfulldirt();
|
||||||
c = term.c;
|
|
||||||
for (i = 0; i < 2; i++) {
|
|
||||||
if (mincol < col && 0 < minrow) {
|
|
||||||
tclearregion(mincol, 0, col - 1, minrow - 1);
|
|
||||||
}
|
|
||||||
if (0 < col && minrow < row) {
|
|
||||||
tclearregion(0, minrow, col - 1, row - 1);
|
|
||||||
}
|
|
||||||
tswapscreen();
|
|
||||||
tcursor(CURSOR_LOAD);
|
|
||||||
}
|
|
||||||
term.c = c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -2647,14 +2764,15 @@ resettitle(void)
|
|||||||
void
|
void
|
||||||
drawregion(int x1, int y1, int x2, int y2)
|
drawregion(int x1, int y1, int x2, int y2)
|
||||||
{
|
{
|
||||||
int y;
|
int y, L;
|
||||||
|
|
||||||
|
L = TLINEOFFSET(y1);
|
||||||
for (y = y1; y < y2; y++) {
|
for (y = y1; y < y2; y++) {
|
||||||
if (!term.dirty[y])
|
if (term.dirty[y]) {
|
||||||
continue;
|
term.dirty[y] = 0;
|
||||||
|
xdrawline(TSCREEN.buffer[L], x1, y, x2);
|
||||||
term.dirty[y] = 0;
|
}
|
||||||
xdrawline(term.line[y], x1, y, x2);
|
L = (L + 1) % TSCREEN.size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2669,14 +2787,15 @@ draw(void)
|
|||||||
/* adjust cursor position */
|
/* adjust cursor position */
|
||||||
LIMIT(term.ocx, 0, term.col-1);
|
LIMIT(term.ocx, 0, term.col-1);
|
||||||
LIMIT(term.ocy, 0, term.row-1);
|
LIMIT(term.ocy, 0, term.row-1);
|
||||||
if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY)
|
if (TLINE(term.ocy)[term.ocx].mode & ATTR_WDUMMY)
|
||||||
term.ocx--;
|
term.ocx--;
|
||||||
if (term.line[term.c.y][cx].mode & ATTR_WDUMMY)
|
if (TLINE(term.c.y)[cx].mode & ATTR_WDUMMY)
|
||||||
cx--;
|
cx--;
|
||||||
|
|
||||||
drawregion(0, 0, term.col, term.row);
|
drawregion(0, 0, term.col, term.row);
|
||||||
xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
|
if (TSCREEN.off == 0)
|
||||||
term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
|
xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
|
||||||
|
term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]);
|
||||||
term.ocx = cx;
|
term.ocx = cx;
|
||||||
term.ocy = term.c.y;
|
term.ocy = term.c.y;
|
||||||
xfinishdraw();
|
xfinishdraw();
|
||||||
|
1
st.h
1
st.h
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
|
#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
|
||||||
#define IS_TRUECOL(x) (1 << 24 & (x))
|
#define IS_TRUECOL(x) (1 << 24 & (x))
|
||||||
|
#define HISTSIZE 2000
|
||||||
|
|
||||||
enum glyph_attribute {
|
enum glyph_attribute {
|
||||||
ATTR_NULL = 0,
|
ATTR_NULL = 0,
|
||||||
|
2
x.c
2
x.c
@ -61,6 +61,8 @@ static void zoomreset(const Arg *);
|
|||||||
static void ttysend(const Arg *);
|
static void ttysend(const Arg *);
|
||||||
static void nextscheme(const Arg *);
|
static void nextscheme(const Arg *);
|
||||||
static void selectscheme(const Arg *);
|
static void selectscheme(const Arg *);
|
||||||
|
void kscrollup(const Arg *);
|
||||||
|
void kscrolldown(const Arg *);
|
||||||
|
|
||||||
/* config.h for applying patches and the configuration. */
|
/* config.h for applying patches and the configuration. */
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user