static void resizeline(Terminal *, termline *, int);
static termline *lineptr(Terminal *, int, int, int);
static void unlineptr(termline *);
+static void check_line_size(Terminal *, termline *);
static void do_paint(Terminal *, Context, int);
static void erase_lots(Terminal *, int, int, int);
static int find_last_nonempty_line(Terminal *, tree234 *);
}
assert(line != NULL);
- resizeline(term, line, term->cols);
- /* FIXME: should we sort the compressed scrollback out here? */
+ /*
+ * Here we resize lines to _at least_ the right length, but we
+ * don't truncate them. Truncation is done as a side effect of
+ * modifying the line.
+ *
+ * The point of this policy is to try to arrange that resizing the
+ * terminal window repeatedly - e.g. successive steps in an X11
+ * opaque window-resize drag, or resizing as a side effect of
+ * retiling by tiling WMs such as xmonad - does not throw away
+ * data gratuitously. Specifically, we want a sequence of resize
+ * operations with no terminal output between them to have the
+ * same effect as a single resize to the ultimate terminal size,
+ * and also (for the case in which xmonad narrows a window that's
+ * scrolling things) we want scrolling up new text at the bottom
+ * of a narrowed window to avoid truncating lines further up when
+ * the window is re-widened.
+ */
+ if (term->cols > line->cols)
+ resizeline(term, line, term->cols);
return line;
}
#define lineptr(x) (lineptr)(term,x,__LINE__,FALSE)
#define scrlineptr(x) (lineptr)(term,x,__LINE__,TRUE)
+/*
+ * Coerce a termline to the terminal's current width. Unlike the
+ * optional resize in lineptr() above, this is potentially destructive
+ * of text, since it can shrink as well as grow the line.
+ *
+ * We call this whenever a termline is actually going to be modified.
+ * Helpfully, putting a single call to this function in check_boundary
+ * deals with _nearly_ all such cases, leaving only a few things like
+ * bulk erase and ESC#8 to handle separately.
+ */
+static void check_line_size(Terminal *term, termline *line)
+{
+ if (term->cols != line->cols) /* trivial optimisation */
+ resizeline(term, line, term->cols);
+}
+
static void term_schedule_tblink(Terminal *term);
static void term_schedule_cblink(Terminal *term);
{
power_on(term, clear);
if (term->ldisc) /* cause ldisc to notice changes */
- ldisc_send(term->ldisc, NULL, 0, 0);
+ ldisc_echoedit_update(term->ldisc);
term->disptop = 0;
deselect(term);
term_update(term);
void term_clrsb(Terminal *term)
{
unsigned char *line;
+ int i;
+
+ /*
+ * Scroll forward to the current screen, if we were back in the
+ * scrollback somewhere until now.
+ */
term->disptop = 0;
+
+ /*
+ * Clear the actual scrollback.
+ */
while ((line = delpos234(term->scrollback, 0)) != NULL) {
sfree(line); /* this is compressed data, not a termline */
}
+
+ /*
+ * When clearing the scrollback, we also truncate any termlines on
+ * the current screen which have remembered data from a previous
+ * larger window size. Rationale: clearing the scrollback is
+ * sometimes done to protect privacy, so the user intention is
+ * specifically that we should not retain evidence of what
+ * previously happened in the terminal, and that ought to include
+ * evidence to the right as well as evidence above.
+ */
+ for (i = 0; i < term->rows; i++)
+ check_line_size(term, scrlineptr(i));
+
+ /*
+ * There are now no lines of real scrollback which can be pulled
+ * back into the screen by a resize, and no lines of the alternate
+ * screen which should be displayed as if part of the scrollback.
+ */
term->tempsblines = 0;
term->alt_sblines = 0;
+
+ /*
+ * Update the scrollbar to reflect the new state of the world.
+ */
update_sbar(term);
}
return;
ldata = scrlineptr(y);
+ check_line_size(term, ldata);
if (x == term->cols) {
ldata->lattr &= ~LATTR_WRAPPED2;
} else {
} else {
termline *ldata = scrlineptr(start.y);
while (poslt(start, end)) {
+ check_line_size(term, ldata);
if (start.x == term->cols) {
if (!erase_lattr)
ldata->lattr &= ~(LATTR_WRAPPED | LATTR_WRAPPED2);
case 10: /* DECEDM: set local edit mode */
term->term_editing = state;
if (term->ldisc) /* cause ldisc to notice changes */
- ldisc_send(term->ldisc, NULL, 0, 0);
+ ldisc_echoedit_update(term->ldisc);
break;
case 25: /* DECTCEM: enable/disable cursor */
compatibility2(OTHER, VT220);
case 12: /* SRM: set echo mode */
term->term_echoing = !state;
if (term->ldisc) /* cause ldisc to notice changes */
- ldisc_send(term->ldisc, NULL, 0, 0);
+ ldisc_echoedit_update(term->ldisc);
break;
case 20: /* LNM: Return sends ... */
term->cr_lf_return = state;
compatibility(VT100);
power_on(term, TRUE);
if (term->ldisc) /* cause ldisc to notice changes */
- ldisc_send(term->ldisc, NULL, 0, 0);
+ ldisc_echoedit_update(term->ldisc);
if (term->reset_132) {
if (!term->no_remote_resize)
request_resize(term->frontend, 80, term->rows);
for (i = 0; i < term->rows; i++) {
ldata = scrlineptr(i);
+ check_line_size(term, ldata);
for (j = 0; j < term->cols; j++) {
copy_termchar(ldata, j,
&term->basic_erase_char);
compatibility(VT100);
{
int nlattr;
+ termline *ldata;
switch (ANSI(c, term->esc_query)) {
case ANSI('3', '#'): /* DECDHL: 2*height, top */
nlattr = LATTR_WIDE;
break;
}
- scrlineptr(term->curs.y)->lattr = nlattr;
+ ldata = scrlineptr(term->curs.y);
+ check_line_size(term, ldata);
+ ldata->lattr = nlattr;
}
break;
/* GZD4: G0 designate 94-set */