📄 terminal.c
字号:
void term_free(Terminal *term)
{
unsigned long *line;
struct beeptime *beep;
while ((line = delpos234(term->scrollback, 0)) != NULL)
sfree(line);
freetree234(term->scrollback);
while ((line = delpos234(term->screen, 0)) != NULL)
sfree(line);
freetree234(term->screen);
while ((line = delpos234(term->alt_screen, 0)) != NULL)
sfree(line);
freetree234(term->alt_screen);
sfree(term->disptext);
while (term->beephead) {
beep = term->beephead;
term->beephead = beep->next;
sfree(beep);
}
bufchain_clear(&term->inbuf);
if(term->print_job)
printer_finish_job(term->print_job);
bufchain_clear(&term->printer_buf);
sfree(term->paste_buffer);
sfree(term);
}
/*
* Set up the terminal for a given size.
*/
void term_size(Terminal *term, int newrows, int newcols, int newsavelines)
{
tree234 *newalt;
unsigned long *newdisp, *line;
int i, j;
int sblen;
int save_alt_which = term->alt_which;
if (newrows == term->rows && newcols == term->cols &&
newsavelines == term->savelines)
return; /* nothing to do */
deselect(term);
swap_screen(term, 0, FALSE, FALSE);
term->alt_t = term->marg_t = 0;
term->alt_b = term->marg_b = newrows - 1;
if (term->rows == -1) {
term->scrollback = newtree234(NULL);
term->screen = newtree234(NULL);
term->tempsblines = 0;
term->rows = 0;
}
/*
* Resize the screen and scrollback. We only need to shift
* lines around within our data structures, because lineptr()
* will take care of resizing each individual line if
* necessary. So:
*
* - If the new screen is longer, we shunt lines in from temporary
* scrollback if possible, otherwise we add new blank lines at
* the bottom.
*
* - If the new screen is shorter, we remove any blank lines at
* the bottom if possible, otherwise shunt lines above the cursor
* to scrollback if possible, otherwise delete lines below the
* cursor.
*
* - Then, if the new scrollback length is less than the
* amount of scrollback we actually have, we must throw some
* away.
*/
sblen = count234(term->scrollback);
/* Do this loop to expand the screen if newrows > rows */
assert(term->rows == count234(term->screen));
while (term->rows < newrows) {
if (term->tempsblines > 0) {
/* Insert a line from the scrollback at the top of the screen. */
assert(sblen >= term->tempsblines);
line = delpos234(term->scrollback, --sblen);
term->tempsblines -= 1;
addpos234(term->screen, line, 0);
term->curs.y += 1;
term->savecurs.y += 1;
} else {
/* Add a new blank line at the bottom of the screen. */
line = snewn(newcols + 2, TTYPE);
line[0] = newcols;
for (j = 0; j < newcols; j++)
line[j + 1] = ERASE_CHAR;
line[newcols + 1] = LATTR_NORM;
addpos234(term->screen, line, count234(term->screen));
}
term->rows += 1;
}
/* Do this loop to shrink the screen if newrows < rows */
while (term->rows > newrows) {
if (term->curs.y < term->rows - 1) {
/* delete bottom row, unless it contains the cursor */
sfree(delpos234(term->screen, term->rows - 1));
} else {
/* push top row to scrollback */
line = delpos234(term->screen, 0);
addpos234(term->scrollback, line, sblen++);
term->tempsblines += 1;
term->curs.y -= 1;
term->savecurs.y -= 1;
}
term->rows -= 1;
}
assert(term->rows == newrows);
assert(count234(term->screen) == newrows);
/* Delete any excess lines from the scrollback. */
while (sblen > newsavelines) {
line = delpos234(term->scrollback, 0);
sfree(line);
sblen--;
}
if (sblen < term->tempsblines)
term->tempsblines = sblen;
assert(count234(term->scrollback) <= newsavelines);
assert(count234(term->scrollback) >= term->tempsblines);
term->disptop = 0;
/* Make a new displayed text buffer. */
newdisp = snewn(newrows * (newcols + 1), TTYPE);
for (i = 0; i < newrows * (newcols + 1); i++)
newdisp[i] = ATTR_INVALID;
sfree(term->disptext);
term->disptext = newdisp;
term->dispcurs = NULL;
/* Make a new alternate screen. */
newalt = newtree234(NULL);
for (i = 0; i < newrows; i++) {
line = snewn(newcols + 2, TTYPE);
line[0] = newcols;
for (j = 0; j < newcols; j++)
line[j + 1] = term->erase_char;
line[newcols + 1] = LATTR_NORM;
addpos234(newalt, line, i);
}
if (term->alt_screen) {
while (NULL != (line = delpos234(term->alt_screen, 0)))
sfree(line);
freetree234(term->alt_screen);
}
term->alt_screen = newalt;
term->alt_sblines = 0;
term->tabs = sresize(term->tabs, newcols, unsigned char);
{
int i;
for (i = (term->cols > 0 ? term->cols : 0); i < newcols; i++)
term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
}
/* Check that the cursor positions are still valid. */
if (term->savecurs.y < 0)
term->savecurs.y = 0;
if (term->savecurs.y >= newrows)
term->savecurs.y = newrows - 1;
if (term->curs.y < 0)
term->curs.y = 0;
if (term->curs.y >= newrows)
term->curs.y = newrows - 1;
if (term->curs.x >= newcols)
term->curs.x = newcols - 1;
term->alt_x = term->alt_y = 0;
term->wrapnext = term->alt_wnext = FALSE;
term->rows = newrows;
term->cols = newcols;
term->savelines = newsavelines;
fix_cpos;
swap_screen(term, save_alt_which, FALSE, FALSE);
update_sbar(term);
term_update(term);
if (term->resize_fn)
term->resize_fn(term->resize_ctx, term->cols, term->rows);
}
/*
* Hand a function and context pointer to the terminal which it can
* use to notify a back end of resizes.
*/
void term_provide_resize_fn(Terminal *term,
void (*resize_fn)(void *, int, int),
void *resize_ctx)
{
term->resize_fn = resize_fn;
term->resize_ctx = resize_ctx;
if (term->cols > 0 && term->rows > 0)
resize_fn(resize_ctx, term->cols, term->rows);
}
/* Find the bottom line on the screen that has any content.
* If only the top line has content, returns 0.
* If no lines have content, return -1.
*/
static int find_last_nonempty_line(Terminal * term, tree234 * screen)
{
int i;
for (i = count234(screen) - 1; i >= 0; i--) {
unsigned long *line = index234(screen, i);
int j;
int cols = line[0];
for (j = 0; j < cols; j++) {
if (line[j + 1] != term->erase_char) break;
}
if (j != cols) break;
}
return i;
}
/*
* Swap screens. If `reset' is TRUE and we have been asked to
* switch to the alternate screen, we must bring most of its
* configuration from the main screen and erase the contents of the
* alternate screen completely. (This is even true if we're already
* on it! Blame xterm.)
*/
static void swap_screen(Terminal *term, int which, int reset, int keep_cur_pos)
{
int t;
tree234 *ttr;
if (!which)
reset = FALSE; /* do no weird resetting if which==0 */
if (which != term->alt_which) {
term->alt_which = which;
ttr = term->alt_screen;
term->alt_screen = term->screen;
term->screen = ttr;
term->alt_sblines = find_last_nonempty_line(term, term->alt_screen) + 1;
t = term->curs.x;
if (!reset && !keep_cur_pos)
term->curs.x = term->alt_x;
term->alt_x = t;
t = term->curs.y;
if (!reset && !keep_cur_pos)
term->curs.y = term->alt_y;
term->alt_y = t;
t = term->marg_t;
if (!reset) term->marg_t = term->alt_t;
term->alt_t = t;
t = term->marg_b;
if (!reset) term->marg_b = term->alt_b;
term->alt_b = t;
t = term->dec_om;
if (!reset) term->dec_om = term->alt_om;
term->alt_om = t;
t = term->wrap;
if (!reset) term->wrap = term->alt_wrap;
term->alt_wrap = t;
t = term->wrapnext;
if (!reset) term->wrapnext = term->alt_wnext;
term->alt_wnext = t;
t = term->insert;
if (!reset) term->insert = term->alt_ins;
term->alt_ins = t;
t = term->cset;
if (!reset) term->cset = term->alt_cset;
term->alt_cset = t;
t = term->utf;
if (!reset) term->utf = term->alt_utf;
term->alt_utf = t;
t = term->sco_acs;
if (!reset) term->sco_acs = term->alt_sco_acs;
term->alt_sco_acs = t;
}
if (reset && term->screen) {
/*
* Yes, this _is_ supposed to honour background-colour-erase.
*/
erase_lots(term, FALSE, TRUE, TRUE);
}
/*
* This might not be possible if we're called during
* initialisation.
*/
if (term->screen)
fix_cpos;
}
/*
* Update the scroll bar.
*/
static void update_sbar(Terminal *term)
{
int nscroll = sblines(term);
set_sbar(term->frontend, nscroll + term->rows,
nscroll + term->disptop, term->rows);
}
/*
* Check whether the region bounded by the two pointers intersects
* the scroll region, and de-select the on-screen selection if so.
*/
static void check_selection(Terminal *term, pos from, pos to)
{
if (poslt(from, term->selend) && poslt(term->selstart, to))
deselect(term);
}
/*
* Scroll the screen. (`lines' is +ve for scrolling forward, -ve
* for backward.) `sb' is TRUE if the scrolling is permitted to
* affect the scrollback buffer.
*
* NB this function invalidates all pointers into lines of the
* screen data structures. In particular, you MUST call fix_cpos
* after calling scroll() and before doing anything else that
* uses the cpos shortcut pointer.
*/
static void scroll(Terminal *term, int topline, int botline, int lines, int sb)
{
unsigned long *line, *line2;
int i, seltop, olddisptop, shift;
if (topline != 0 || term->alt_which != 0)
sb = FALSE;
olddisptop = term->disptop;
shift = lines;
if (lines < 0) {
while (lines < 0) {
line = delpos234(term->screen, botline);
line = resizeline(line, term->cols);
for (i = 0; i < term->cols; i++)
line[i + 1] = term->erase_char;
line[term->cols + 1] = 0;
addpos234(term->screen, line, topline);
if (term->selstart.y >= topline && term->selstart.y <= botline) {
term->selstart.y++;
if (term->selstart.y > botline) {
term->selstart.y = botline + 1;
term->selstart.x = 0;
}
}
if (term->selend.y >= topline && term->selend.y <= botline) {
term->selend.y++;
if (term->selend.y > botline) {
term->selend.y = botline + 1;
term->selend.x = 0;
}
}
lines++;
}
} else {
while (lines > 0) {
line = delpos234(term->screen, topline);
if (sb && term->savelines > 0) {
int sblen = count234(term->scrollback);
/*
* We must add this line to the scrollback. We'll
* remove a line from the top of the scrollback to
* replace it, or allocate a new one if the
* scrollback isn't full.
*/
if (sblen == term->savelines) {
sblen--, line2 = delpos234(term->scrollback, 0);
} else {
line2 = snewn(term->cols + 2, TTYPE);
line2[0] = term->cols;
term->tempsblines += 1;
}
addpos234(term->scrollback, line, sblen);
line = line2;
/*
* If the user is currently looking at part of the
* scrollback, and they haven't enabled any options
* that are going to reset the scrollback as a
* result of this movement, then the chances are
* they'd like to keep looking at the same line. So
* we move their viewpoint at the same rate as the
* scroll, at least until their viewpoint hits the
* top end of the scrollback buffer, at which point
* we don't have the choice any more.
*
* Thanks to Jan Holmen Holsten for the idea and
* initial implementation.
*/
if (term->disptop > -term->savelines && term->disptop < 0)
term->disptop--;
}
line = resizeline(line, term->cols);
for (i = 0; i < term->cols; i++)
line[i + 1] = term->erase_char;
line[term->cols + 1] = LATTR_NORM;
addpos234(term->screen, line, botline);
/*
* If the selection endpoints move into the scrollback,
* we keep them moving until they hit the top. However,
* of course, if the line _hasn't_ moved into the
* scrollback then we don't do this, and cut them off
* at the top of the scroll region.
*
* This applies to selstart and selend (for an existing
* selection), and also selanchor (for one being
* selected as we speak).
*/
seltop = sb ? -term->savelines : topline;
if (term->selstate != NO_SELECTION) {
if (term->selstart.y >= seltop &&
term->selstart.y <= botline) {
term->selstart.y--;
if (term->selstart.y < seltop) {
term->selstart.y = seltop;
term->selstart.x = 0;
}
}
if (term->selend.y >= seltop && term->selend.y <= botline) {
term->selend.y--;
if (term->selend.y < seltop) {
term->selend.y = seltop;
term->selend.x = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -