}
/*
+ * Get the number of lines in the scrollback.
+ */
+static int sblines(Terminal *term)
+{
+ int sblines = count234(term->scrollback);
+ if (term->cfg.erase_to_scrollback &&
+ term->alt_which && term->alt_screen) {
+ sblines += term->alt_sblines;
+ }
+ return sblines;
+}
+
+/*
* Retrieve a line of the screen or of the scrollback, according to
* whether the y coordinate is non-negative or negative
* (respectively).
whichtree = term->screen;
treeindex = y;
} else {
- whichtree = term->scrollback;
- treeindex = y + count234(term->scrollback);
+ int altlines = 0;
+ if (term->cfg.erase_to_scrollback &&
+ term->alt_which && term->alt_screen) {
+ altlines = term->alt_sblines;
+ }
+ if (y < -altlines) {
+ whichtree = term->scrollback;
+ treeindex = y + altlines + count234(term->scrollback);
+ } else {
+ whichtree = term->alt_screen;
+ treeindex = y + term->alt_sblines;
+ /* treeindex = y + count234(term->alt_screen); */
+ }
}
line = index234(whichtree, treeindex);
while ((line = delpos234(term->scrollback, 0)) != NULL) {
sfree(line);
}
+ term->tempsblines = 0;
+ term->alt_sblines = 0;
update_sbar(term);
}
term->curstype = 0;
term->screen = term->alt_screen = term->scrollback = NULL;
+ term->tempsblines = 0;
+ term->alt_sblines = 0;
term->disptop = 0;
term->disptext = term->dispcurs = NULL;
term->tabs = NULL;
term->rows = term->cols = -1;
power_on(term);
term->beephead = term->beeptail = NULL;
+#ifdef OPTIMISE_SCROLL
+ term->scrollhead = term->scrolltail = NULL;
+#endif /* OPTIMISE_SCROLL */
term->nbeeps = 0;
term->lastbeep = FALSE;
term->beep_overloaded = FALSE;
if (term->rows == -1) {
term->scrollback = newtree234(NULL);
term->screen = newtree234(NULL);
+ term->tempsblines = 0;
term->rows = 0;
}
* will take care of resizing each individual line if
* necessary. So:
*
- * - If the new screen and the old screen differ in length, we
- * must shunt some lines in from the scrollback or out to
- * the scrollback.
- *
- * - If doing that fails to provide us with enough material to
- * fill the new screen (i.e. the number of rows needed in
- * the new screen exceeds the total number in the previous
- * screen+scrollback), we must invent some blank lines to
- * cover the gap.
+ * - 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
*/
sblen = count234(term->scrollback);
/* Do this loop to expand the screen if newrows > rows */
- for (i = term->rows; i < newrows; i++) {
- if (sblen > 0) {
+ 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 = smalloc(TSIZE * (newcols + 2));
line[0] = newcols;
for (j = 0; j < newcols; j++)
line[j + 1] = ERASE_CHAR;
- line[newcols] = LATTR_NORM;
+ line[newcols + 1] = LATTR_NORM;
+ addpos234(term->screen, line, count234(term->screen));
}
- addpos234(term->screen, line, 0);
+ term->rows += 1;
}
/* Do this loop to shrink the screen if newrows < rows */
- for (i = newrows; i < term->rows; i++) {
- line = delpos234(term->screen, 0);
- addpos234(term->scrollback, line, sblen++);
+ 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 = smalloc(newrows * (newcols + 1) * TSIZE);
for (i = 0; i < newrows * (newcols + 1); i++)
newdisp[i] = ATTR_INVALID;
term->disptext = newdisp;
term->dispcurs = NULL;
+ /* Make a new alternate screen. */
newalt = newtree234(NULL);
for (i = 0; i < newrows; i++) {
line = smalloc(TSIZE * (newcols + 2));
freetree234(term->alt_screen);
}
term->alt_screen = newalt;
+ term->alt_sblines = 0;
term->tabs = srealloc(term->tabs, newcols * sizeof(*term->tabs));
{
term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
}
- if (term->rows > 0)
- term->curs.y += newrows - term->rows;
+ /* 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)
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
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;
*/
static void update_sbar(Terminal *term)
{
- int nscroll;
-
- nscroll = count234(term->scrollback);
-
+ int nscroll = sblines(term);
set_sbar(term->frontend, nscroll + term->rows,
nscroll + term->disptop, term->rows);
}
} else {
line2 = smalloc(TSIZE * (term->cols + 2));
line2[0] = term->cols;
+ term->tempsblines += 1;
}
addpos234(term->scrollback, line, sblen);
line = line2;
#ifdef OPTIMISE_SCROLL
/*
+ * Add a scroll of a region on the screen into the pending scroll list.
+ * `lines' is +ve for scrolling forward, -ve for backward.
+ *
+ * If the scroll is on the same area as the last scroll in the list,
+ * merge them.
+ */
+static void save_scroll(Terminal *term, int topline, int botline, int lines)
+{
+ struct scrollregion *newscroll;
+ if (term->scrolltail &&
+ term->scrolltail->topline == topline &&
+ term->scrolltail->botline == botline) {
+ term->scrolltail->lines += lines;
+ } else {
+ newscroll = smalloc(sizeof(struct scrollregion));
+ newscroll->topline = topline;
+ newscroll->botline = botline;
+ newscroll->lines = lines;
+ newscroll->next = NULL;
+
+ if (!term->scrollhead)
+ term->scrollhead = newscroll;
+ else
+ term->scrolltail->next = newscroll;
+ term->scrolltail = newscroll;
+ }
+}
+
+/*
* Scroll the physical display, and our conception of it in disptext.
*/
static void scroll_display(Terminal *term, int topline, int botline, int lines)
for (i = 0; i < distance; i++)
start[i] |= ATTR_INVALID;
}
- do_scroll(term->frontend, topline, botline, lines);
+ save_scroll(term, topline, botline, lines);
}
#endif /* OPTIMISE_SCROLL */
{
pos start, end;
int erase_lattr;
- unsigned long *ldata;
+ int erasing_lines_from_top = 0;
if (line_only) {
start.y = term->curs.y;
if (start.y == 0 && start.x == 0 && end.y == term->rows)
term_invalidate(term);
- ldata = lineptr(start.y);
- while (poslt(start, end)) {
- if (start.x == term->cols) {
- if (erase_lattr)
- ldata[start.x] &= ~(LATTR_WRAPPED | LATTR_WRAPPED2);
- } else {
- ldata[start.x] = term->erase_char;
- }
- if (incpos(start) && start.y < term->rows)
- ldata = lineptr(start.y);
+ /* Lines scrolled away shouldn't be brought back on if the terminal
+ * resizes. */
+ if (start.y == 0 && start.x == 0 && end.x == 0 && erase_lattr)
+ erasing_lines_from_top = 1;
+
+ if (term->cfg.erase_to_scrollback && erasing_lines_from_top) {
+ /* If it's a whole number of lines, starting at the top, and
+ * we're fully erasing them, erase by scrolling and keep the
+ * lines in the scrollback. */
+ int scrolllines = end.y;
+ if (end.y == term->rows) {
+ /* Shrink until we find a non-empty row.*/
+ scrolllines = find_last_nonempty_line(term, term->screen) + 1;
+ }
+ if (scrolllines > 0)
+ scroll(term, 0, scrolllines - 1, scrolllines, TRUE);
+ fix_cpos;
+ } else {
+ unsigned long *ldata = lineptr(start.y);
+ while (poslt(start, end)) {
+ if (start.x == term->cols) {
+ if (!erase_lattr)
+ ldata[start.x] &= ~(LATTR_WRAPPED | LATTR_WRAPPED2);
+ else
+ ldata[start.x] = LATTR_NORM;
+ } else {
+ ldata[start.x] = term->erase_char;
+ }
+ if (incpos(start) && start.y < term->rows)
+ ldata = lineptr(start.y);
+ }
}
+
+ /* After an erase of lines from the top of the screen, we shouldn't
+ * bring the lines back again if the terminal enlarges (since the user or
+ * application has explictly thrown them away). */
+ if (erasing_lines_from_top && !(term->alt_which))
+ term->tempsblines = 0;
}
/*
char ch[1024];
long cursor_background = ERASE_CHAR;
unsigned long ticks;
+#ifdef OPTIMISE_SCROLL
+ struct scrollregion *sr;
+#endif /* OPTIMISE_SCROLL */
/*
* Check the visual bell state.
}
term->dispcurs = NULL;
+#ifdef OPTIMISE_SCROLL
+ /* Do scrolls */
+ sr = term->scrollhead;
+ while (sr) {
+ struct scrollregion *next = sr->next;
+ do_scroll(ctx, sr->topline, sr->botline, sr->lines);
+ sfree(sr);
+ sr = next;
+ }
+ term->scrollhead = term->scrolltail = NULL;
+#endif /* OPTIMISE_SCROLL */
+
/* The normal screen data */
for (i = 0; i < term->rows; i++) {
unsigned long *ldata;
*/
void term_scroll(Terminal *term, int rel, int where)
{
- int sbtop = -count234(term->scrollback);
+ int sbtop = -sblines(term);
#ifdef OPTIMISE_SCROLL
int olddisptop = term->disptop;
int shift;
void term_copyall(Terminal *term)
{
pos top;
- top.y = -count234(term->scrollback);
+ top.y = -sblines(term);
top.x = 0;
clipme(term, top, term->curs, 0);
}
{
unsigned long *ldata;
short wvalue;
- int topy = -count234(term->scrollback);
+ int topy = -sblines(term);
ldata = lineptr(p.y);
char output[10];
char *p = output;
int prependesc = FALSE;
+#if 0
int i;
fprintf(stderr, "keysym = %d, %d chars:", keysym, tlen);
for (i = 0; i < tlen; i++)
- fprintf(stderr, " %04x", text[i]);
+ fprintf(stderr, " %04x", (unsigned)text[i]);
fprintf(stderr, "\n");
+#endif
/* XXX Num Lock */
if ((flags & PKF_REPEAT) && term->repeat_off)
if (keysym == PK_NULL && (modifiers & PKM_CONTROL) && tlen == 1 &&
text[0] >= 0x20 && text[0] <= 0x7e) {
/* ASCII chars + Control */
- if (text[0] >= 0x40 && text[0] <= 0x5f ||
- text[0] >= 0x61 && text[0] <= 0x7a)
+ if ((text[0] >= 0x40 && text[0] <= 0x5f) ||
+ (text[0] >= 0x61 && text[0] <= 0x7a))
text[0] &= 0x1f;
else {
/*
case PK_KP7: c = 'y'; break;
case PK_KP8: c = 'k'; break;
case PK_KP9: c = 'u'; break;
+ default: break; /* else gcc warns `enum value not used' */
}
if (c != 0) {
if (c != '.') {
case PK_PF2: xkey = 'Q'; break;
case PK_PF3: xkey = 'R'; break;
case PK_PF4: xkey = 'S'; break;
+ default: break; /* else gcc warns `enum value not used' */
}
}
if (term->app_keypad_keys && !term->cfg.no_applic_k) {
case PK_KP9: xkey = 'y'; break;
case PK_KPDECIMAL: xkey = 'n'; break;
case PK_KPENTER: xkey = 'M'; break;
+ default: break; /* else gcc warns `enum value not used' */
}
if (term->cfg.funky_type == FUNKY_XTERM && tlen > 0) {
/*
break;
case PK_KPMINUS: xkey = 'm'; break;
case PK_KPCOMMA: xkey = 'l'; break;
+ default: break; /* else gcc warns `enum value not used' */
}
}
}
case PK_KP7: keysym = PK_HOME; break;
case PK_KP8: keysym = PK_UP; break;
case PK_KP9: keysym = PK_PAGEUP; break;
+ default: break; /* else gcc warns `enum value not used' */
}
}
}
*p++ = 0x0a;
goto done;
}
+ default: break; /* else gcc warns `enum value not used' */
}
/* SCO function keys and editing keys */
case PK_END: xkey = 'F'; break;
case PK_PAGEUP: xkey = 'I'; break;
case PK_PAGEDOWN: xkey = 'G'; break;
+ default: break; /* else gcc warns `enum value not used' */
}
p += sprintf((char *) p, "\x1B[%c", xkey);
}
case PK_END: keysym = PK_PAGEUP; break;
case PK_PAGEUP: keysym = PK_DELETE; break;
case PK_PAGEDOWN: keysym = PK_PAGEDOWN; break;
+ default: break; /* else gcc warns `enum value not used' */
}
}
case PK_END: xkey = 'E'; break;
case PK_PAGEUP: xkey = 'I'; break;
case PK_PAGEDOWN: xkey = 'G'; break;
+ default: break; /* else gcc warns `enum value not used' */
}
p += sprintf((char *) p, "\x1B%c", xkey);
goto done;
case PK_END: code = 4; break;
case PK_PAGEUP: code = 5; break;
case PK_PAGEDOWN: code = 6; break;
+ default: break; /* else gcc warns `enum value not used' */
}
p += sprintf((char *) p, "\x1B[%d~", code);
goto done;
case PK_RIGHT: xkey = 'C'; break;
case PK_LEFT: xkey = 'D'; break;
case PK_REST: xkey = 'G'; break; /* centre key on number pad */
+ default: break; /* else gcc warns `enum value not used' */
}
if (term->vt52_mode)
p += sprintf((char *) p, "\x1B%c", xkey);
term_seen_key_event(term);
if (prependesc) {
+#if 0
fprintf(stderr, "sending ESC\n");
+#endif
ldisc_send(term->ldisc, "\x1b", 1, 1);
}
if (p > output) {
+#if 0
fprintf(stderr, "sending %d bytes:", p - output);
for (i = 0; i < p - output; i++)
fprintf(stderr, " %02x", output[i]);
fprintf(stderr, "\n");
-
+#endif
ldisc_send(term->ldisc, output, p - output, 1);
} else if (tlen > 0) {
+#if 0
fprintf(stderr, "sending %d unichars:", tlen);
for (i = 0; i < tlen; i++)
- fprintf(stderr, " %04x", text[i]);
+ fprintf(stderr, " %04x", (unsigned) text[i]);
fprintf(stderr, "\n");
-
+#endif
luni_send(term->ldisc, text, tlen, 1);
}
}
/*
* from_backend(), to get data from the backend for the terminal.
*/
-int from_backend(void *vterm, int is_stderr, char *data, int len)
+int from_backend(void *vterm, int is_stderr, const char *data, int len)
{
Terminal *term = (Terminal *)vterm;