#define has_compat(x) ( ((CL_##x)&term->compatibility_level) != 0 )
+const char sco2ansicolour[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+
#define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
const wchar_t sel_nl[] = SEL_NL;
/*
+ * Fetch the character at a particular position in a line array,
+ * for purposes of `wordtype'. The reason this isn't just a simple
+ * array reference is that if the character we find is UCSWIDE,
+ * then we must look one space further to the left.
+ */
+#define UCSGET(a, x) \
+ ( (x)>0 && ((a)[(x)] & (CHAR_MASK | CSET_MASK)) == UCSWIDE ? \
+ (a)[(x)-1] : (a)[(x)] )
+
+/*
* Internal prototypes.
*/
static unsigned long *resizeline(unsigned long *, int);
*/
oldlen = line[0];
lineattrs = line[oldlen + 1];
- line = srealloc(line, TSIZE * (2 + cols));
+ line = sresize(line, 2 + cols, TTYPE);
line[0] = cols;
for (i = oldlen; i < cols; i++)
line[i + 1] = ERASE_CHAR;
}
/*
+ * 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);
for (i = 0; i < term->cols; i++)
term->tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
}
- term->alt_om = term->dec_om = term->cfg->dec_om;
+ term->alt_om = term->dec_om = term->cfg.dec_om;
term->alt_ins = term->insert = FALSE;
term->alt_wnext = term->wrapnext = term->save_wnext = FALSE;
- term->alt_wrap = term->wrap = term->cfg->wrap_mode;
+ term->alt_wrap = term->wrap = term->cfg.wrap_mode;
term->alt_cset = term->cset = term->save_cset = 0;
term->alt_utf = term->utf = term->save_utf = 0;
term->utf_state = 0;
term->in_vbell = FALSE;
term->cursor_on = 1;
term->big_cursor = 0;
- term->save_attr = term->curr_attr = ATTR_DEFAULT;
+ term->default_attr = term->save_attr = term->curr_attr = ATTR_DEFAULT;
term->term_editing = term->term_echoing = FALSE;
- term->app_cursor_keys = term->cfg->app_cursor;
- term->app_keypad_keys = term->cfg->app_keypad;
- term->use_bce = term->cfg->bce;
- term->blink_is_real = term->cfg->blinktext;
+ term->app_cursor_keys = term->cfg.app_cursor;
+ term->app_keypad_keys = term->cfg.app_keypad;
+ term->use_bce = term->cfg.bce;
+ term->blink_is_real = term->cfg.blinktext;
term->erase_char = ERASE_CHAR;
term->alt_which = 0;
term_print_finish(term);
{
int i;
for (i = 0; i < 256; i++)
- term->wordness[i] = term->cfg->wordness[i];
+ term->wordness[i] = term->cfg.wordness[i];
}
if (term->screen) {
swap_screen(term, 1, FALSE, FALSE);
ctx = get_ctx(term->frontend);
if (ctx) {
int need_sbar_update = term->seen_disp_event;
- if (term->seen_disp_event && term->cfg->scroll_on_disp) {
+ if (term->seen_disp_event && term->cfg.scroll_on_disp) {
term->disptop = 0; /* return to main screen */
term->seen_disp_event = 0;
need_sbar_update = TRUE;
/*
* Reset the scrollback on keypress, if we're doing that.
*/
- if (term->cfg->scroll_on_key) {
+ if (term->cfg.scroll_on_key) {
term->disptop = 0; /* return to main screen */
term->seen_disp_event = 1;
}
* user has disabled mouse reporting, and abandon a print job if
* the user has disabled printing.
*/
-void term_reconfig(Terminal *term)
+void term_reconfig(Terminal *term, Config *cfg)
{
- if (term->cfg->no_alt_screen)
+ /*
+ * Before adopting the new config, check all those terminal
+ * settings which control power-on defaults; and if they've
+ * changed, we will modify the current state as well as the
+ * default one. The full list is: Auto wrap mode, DEC Origin
+ * Mode, BCE, blinking text, character classes.
+ */
+ int reset_wrap, reset_decom, reset_bce, reset_blink, reset_charclass;
+ int i;
+
+ reset_wrap = (term->cfg.wrap_mode != cfg->wrap_mode);
+ reset_decom = (term->cfg.dec_om != cfg->dec_om);
+ reset_bce = (term->cfg.bce != cfg->bce);
+ reset_blink = (term->cfg.blinktext != cfg->blinktext);
+ reset_charclass = 0;
+ for (i = 0; i < lenof(term->cfg.wordness); i++)
+ if (term->cfg.wordness[i] != cfg->wordness[i])
+ reset_charclass = 1;
+
+ term->cfg = *cfg; /* STRUCTURE COPY */
+
+ if (reset_wrap)
+ term->alt_wrap = term->wrap = term->cfg.wrap_mode;
+ if (reset_decom)
+ term->alt_om = term->dec_om = term->cfg.dec_om;
+ if (reset_bce) {
+ term->use_bce = term->cfg.bce;
+ if (term->use_bce)
+ term->erase_char = (' ' | ATTR_ASCII |
+ (term->curr_attr &
+ (ATTR_FGMASK | ATTR_BGMASK)));
+ else
+ term->erase_char = ERASE_CHAR;
+ }
+ if (reset_blink)
+ term->blink_is_real = term->cfg.blinktext;
+ if (reset_charclass)
+ for (i = 0; i < 256; i++)
+ term->wordness[i] = term->cfg.wordness[i];
+
+ if (term->cfg.no_alt_screen)
swap_screen(term, 0, FALSE, FALSE);
- if (term->cfg->no_mouse_rep) {
+ if (term->cfg.no_mouse_rep) {
term->xterm_mouse = 0;
set_raw_mouse_mode(term->frontend, 0);
}
- if (term->cfg->no_remote_charset) {
+ if (term->cfg.no_remote_charset) {
term->cset_attr[0] = term->cset_attr[1] = ATTR_ASCII;
term->sco_acs = term->alt_sco_acs = 0;
term->utf = 0;
}
- if (!*term->cfg->printer) {
+ if (!*term->cfg.printer) {
term_print_finish(term);
}
}
while ((line = delpos234(term->scrollback, 0)) != NULL) {
sfree(line);
}
+ term->tempsblines = 0;
+ term->alt_sblines = 0;
update_sbar(term);
}
/*
* Initialise the terminal.
*/
-Terminal *term_init(Config *mycfg, void *frontend)
+Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata,
+ void *frontend)
{
Terminal *term;
* Allocate a new Terminal structure and initialise the fields
* that need it.
*/
- term = smalloc(sizeof(Terminal));
+ term = snew(Terminal);
term->frontend = frontend;
- term->cfg = mycfg;
+ term->ucsdata = ucsdata;
+ term->cfg = *mycfg; /* STRUCTURE COPY */
term->logctx = NULL;
term->compatibility_level = TM_PUTTY;
strcpy(term->id_string, "\033[?6c");
term->last_blink = term->last_tblink = 0;
term->paste_buffer = NULL;
+ term->paste_len = 0;
term->last_paste = 0;
bufchain_init(&term->inbuf);
bufchain_init(&term->printer_buf);
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;
term->attr_mask = 0xffffffff;
term->resize_fn = NULL;
term->resize_ctx = NULL;
+ term->in_term_out = FALSE;
return term;
}
+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.
*/
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 {
- line = smalloc(TSIZE * (newcols + 2));
+ /* 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++)
+ for (j = 0; j < newcols; j++)
line[j + 1] = ERASE_CHAR;
+ 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;
- newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
+ /* 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 = smalloc(TSIZE * (newcols + 2));
+ line = snewn(newcols + 2, TTYPE);
line[0] = newcols;
- for (j = 0; j <= newcols; j++)
+ 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) {
freetree234(term->alt_screen);
}
term->alt_screen = newalt;
+ term->alt_sblines = 0;
- term->tabs = srealloc(term->tabs, newcols * sizeof(*term->tabs));
+ 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);
}
- 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);
}
if (term->selstart.y >= topline && term->selstart.y <= botline) {
term->selstart.y++;
if (term->selstart.y > botline) {
- 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;
+ term->selend.y = botline + 1;
term->selend.x = 0;
}
}
if (sblen == term->savelines) {
sblen--, line2 = delpos234(term->scrollback, 0);
} else {
- line2 = smalloc(TSIZE * (term->cols + 2));
+ line2 = snewn(term->cols + 2, TTYPE);
line2[0] = term->cols;
+ term->tempsblines += 1;
}
addpos234(term->scrollback, line, sblen);
line = line2;
line = resizeline(line, term->cols);
for (i = 0; i < term->cols; i++)
line[i + 1] = term->erase_char;
- line[term->cols + 1] = 0;
+ line[term->cols + 1] = LATTR_NORM;
addpos234(term->screen, line, botline);
/*
*/
seltop = sb ? -term->savelines : topline;
- 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->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;
+ 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;
+ }
}
- }
- if (term->selanchor.y >= seltop && term->selanchor.y <= botline) {
- term->selanchor.y--;
- if (term->selanchor.y < seltop) {
- term->selanchor.y = seltop;
- term->selanchor.x = 0;
+ if (term->selanchor.y >= seltop &&
+ term->selanchor.y <= botline) {
+ term->selanchor.y--;
+ if (term->selanchor.y < seltop) {
+ term->selanchor.y = seltop;
+ term->selanchor.x = 0;
+ }
}
}
#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 = snew(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 */
}
/*
+ * This function is called before doing _anything_ which affects
+ * only part of a line of text. It is used to mark the boundary
+ * between two character positions, and it indicates that some sort
+ * of effect is going to happen on only one side of that boundary.
+ *
+ * The effect of this function is to check whether a CJK
+ * double-width character is straddling the boundary, and to remove
+ * it and replace it with two spaces if so. (Of course, one or
+ * other of those spaces is then likely to be replaced with
+ * something else again, as a result of whatever happens next.)
+ *
+ * Also, if the boundary is at the right-hand _edge_ of the screen,
+ * it implies something deliberate is being done to the rightmost
+ * column position; hence we must clear LATTR_WRAPPED2.
+ *
+ * The input to the function is the coordinates of the _second_
+ * character of the pair.
+ */
+static void check_boundary(Terminal *term, int x, int y)
+{
+ unsigned long *ldata;
+
+ /* Validate input coordinates, just in case. */
+ if (x == 0 || x > term->cols)
+ return;
+
+ ldata = lineptr(y);
+ if (x == term->cols) {
+ ldata[x] &= ~LATTR_WRAPPED2;
+ } else {
+ if ((ldata[x] & (CHAR_MASK | CSET_MASK)) == UCSWIDE) {
+ ldata[x-1] = ldata[x] =
+ (ldata[x-1] &~ (CHAR_MASK | CSET_MASK)) | ATTR_ASCII | ' ';
+ }
+ }
+}
+
+/*
* Erase a large portion of the screen: the whole screen, or the
* whole line, or parts thereof.
*/
{
pos start, end;
int erase_lattr;
- unsigned long *ldata;
+ int erasing_lines_from_top = 0;
if (line_only) {
start.y = term->curs.y;
end = term->curs;
incpos(end);
}
+ if (!from_begin || !to_end)
+ check_boundary(term, term->curs.x, term->curs.y);
check_selection(term, start, end);
/* Clear screen also forces a full window redraw, just in case. */
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 && !erase_lattr)
- ldata[start.x] &= ~LATTR_WRAPPED;
- 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;
}
/*
cursplus.y = term->curs.y;
cursplus.x = term->curs.x + n;
check_selection(term, term->curs, cursplus);
+ check_boundary(term, term->curs.x, term->curs.y);
+ if (dir < 0)
+ check_boundary(term, term->curs.x + n, term->curs.y);
ldata = lineptr(term->curs.y);
if (dir < 0) {
memmove(ldata + term->curs.x, ldata + term->curs.x + n, m * TSIZE);
if (query)
switch (mode) {
- case 1: /* application cursor keys */
+ case 1: /* DECCKM: application cursor keys */
term->app_cursor_keys = state;
break;
- case 2: /* VT52 mode */
+ case 2: /* DECANM: VT52 mode */
term->vt52_mode = !state;
if (term->vt52_mode) {
term->blink_is_real = FALSE;
term->vt52_bold = FALSE;
} else {
- term->blink_is_real = term->cfg->blinktext;
+ term->blink_is_real = term->cfg.blinktext;
}
break;
- case 3: /* 80/132 columns */
+ case 3: /* DECCOLM: 80/132 columns */
deselect(term);
- if (!term->cfg->no_remote_resize)
+ if (!term->cfg.no_remote_resize)
request_resize(term->frontend, state ? 132 : 80, term->rows);
term->reset_132 = state;
+ term->alt_t = term->marg_t = 0;
+ term->alt_b = term->marg_b = term->rows - 1;
+ move(term, 0, 0, 0);
+ erase_lots(term, FALSE, TRUE, TRUE);
break;
- case 5: /* reverse video */
+ case 5: /* DECSCNM: reverse video */
/*
* Toggle reverse video. If we receive an OFF within the
* visual bell timeout period after an ON, we trigger an
if (state)
term_update(term);
break;
- case 6: /* DEC origin mode */
+ case 6: /* DECOM: DEC origin mode */
term->dec_om = state;
break;
- case 7: /* auto wrap */
+ case 7: /* DECAWM: auto wrap */
term->wrap = state;
break;
- case 8: /* auto key repeat */
+ case 8: /* DECARM: auto key repeat */
term->repeat_off = !state;
break;
- case 10: /* set local edit mode */
+ 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);
break;
- case 25: /* enable/disable cursor */
+ case 25: /* DECTCEM: enable/disable cursor */
compatibility2(OTHER, VT220);
term->cursor_on = state;
term->seen_disp_event = TRUE;
case 47: /* alternate screen */
compatibility(OTHER);
deselect(term);
- swap_screen(term, term->cfg->no_alt_screen ? 0 : state, FALSE, FALSE);
+ swap_screen(term, term->cfg.no_alt_screen ? 0 : state, FALSE, FALSE);
term->disptop = 0;
break;
case 1000: /* xterm mouse 1 */
case 1047: /* alternate screen */
compatibility(OTHER);
deselect(term);
- swap_screen(term, term->cfg->no_alt_screen ? 0 : state, TRUE, TRUE);
+ swap_screen(term, term->cfg.no_alt_screen ? 0 : state, TRUE, TRUE);
term->disptop = 0;
break;
case 1048: /* save/restore cursor */
- save_cursor(term, state);
+ if (!term->cfg.no_alt_screen)
+ save_cursor(term, state);
if (!state) term->seen_disp_event = TRUE;
break;
case 1049: /* cursor & alternate screen */
- if (state)
+ if (state && !term->cfg.no_alt_screen)
save_cursor(term, state);
if (!state) term->seen_disp_event = TRUE;
compatibility(OTHER);
deselect(term);
- swap_screen(term, term->cfg->no_alt_screen ? 0 : state, TRUE, FALSE);
- if (!state)
+ swap_screen(term, term->cfg.no_alt_screen ? 0 : state, TRUE, FALSE);
+ if (!state && !term->cfg.no_alt_screen)
save_cursor(term, state);
term->disptop = 0;
break;
} else
switch (mode) {
- case 4: /* set insert mode */
+ case 4: /* IRM: set insert mode */
compatibility(VT102);
term->insert = state;
break;
- case 12: /* set echo mode */
+ 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);
break;
- case 20: /* Return sends ... */
+ case 20: /* LNM: Return sends ... */
term->cr_lf_return = state;
break;
- case 34: /* Make cursor BIG */
+ case 34: /* WYULCURM: Make cursor BIG */
compatibility2(OTHER, VT220);
term->big_cursor = !state;
}
switch (term->esc_args[0]) {
case 0:
case 1:
- if (!term->cfg->no_remote_wintitle)
+ if (!term->cfg.no_remote_wintitle)
set_icon(term->frontend, term->osc_string);
if (term->esc_args[0] == 1)
break;
/* fall through: parameter 0 means set both */
case 2:
case 21:
- if (!term->cfg->no_remote_wintitle)
+ if (!term->cfg.no_remote_wintitle)
set_title(term->frontend, term->osc_string);
break;
}
static void term_print_setup(Terminal *term)
{
bufchain_clear(&term->printer_buf);
- term->print_job = printer_start_job(term->cfg->printer);
+ term->print_job = printer_start_job(term->cfg.printer);
}
static void term_print_flush(Terminal *term)
{
* Optionally log the session traffic to a file. Useful for
* debugging and possibly also useful for actual logging.
*/
- if (term->cfg->logtype == LGTYP_DEBUG && term->logctx)
+ if (term->cfg.logtype == LGTYP_DEBUG && term->logctx)
logtraffic(term->logctx, (unsigned char) c, LGTYP_DEBUG);
} else {
c = unget;
case 0:
if (c < 0x80) {
/* UTF-8 must be stateless so we ignore iso2022. */
- if (unitab_ctrl[c] != 0xFF)
- c = unitab_ctrl[c];
+ if (term->ucsdata->unitab_ctrl[c] != 0xFF)
+ c = term->ucsdata->unitab_ctrl[c];
else c = ((unsigned char)c) | ATTR_ASCII;
break;
} else if ((c & 0xe0) == 0xc0) {
* the same encoding.
*/
case ATTR_LINEDRW:
- if (unitab_ctrl[c] != 0xFF)
- c = unitab_ctrl[c];
+ if (term->ucsdata->unitab_ctrl[c] != 0xFF)
+ c = term->ucsdata->unitab_ctrl[c];
else
c = ((unsigned char) c) | ATTR_LINEDRW;
break;
break;
}
/*FALLTHROUGH*/ case ATTR_ASCII:
- if (unitab_ctrl[c] != 0xFF)
- c = unitab_ctrl[c];
+ if (term->ucsdata->unitab_ctrl[c] != 0xFF)
+ c = term->ucsdata->unitab_ctrl[c];
else
c = ((unsigned char) c) | ATTR_ASCII;
break;
term->curs.x--;
term->wrapnext = FALSE;
fix_cpos;
- if (!term->cfg->no_dbackspace) /* destructive bksp might be disabled */
+ if (!term->cfg.no_dbackspace) /* destructive bksp might be disabled */
*term->cpos = (' ' | term->curr_attr | ATTR_ASCII);
} else
/* Or normal C0 controls. */
if ((c & -32) == 0 && term->termstate < DO_CTRLS) {
switch (c) {
- case '\005': /* terminal type query */
+ case '\005': /* ENQ: terminal type query */
/* Strictly speaking this is VT100 but a VT100 defaults to
* no response. Other terminals respond at their option.
*
if (term->ldisc) {
char abuf[256], *s, *d;
int state = 0;
- for (s = term->cfg->answerback, d = abuf; *s; s++) {
+ for (s = term->cfg.answerback, d = abuf; *s; s++) {
if (state) {
if (*s >= 'a' && *s <= 'z')
*d++ = (*s - ('a' - 1));
abuf, d - abuf, 0);
}
break;
- case '\007':
+ case '\007': /* BEL: Bell */
{
struct beeptime *newbeep;
unsigned long ticks;
ticks = GETTICKCOUNT();
if (!term->beep_overloaded) {
- newbeep = smalloc(sizeof(struct beeptime));
+ newbeep = snew(struct beeptime);
newbeep->ticks = ticks;
newbeep->next = NULL;
if (!term->beephead)
* t seconds ago.
*/
while (term->beephead &&
- term->beephead->ticks < ticks - term->cfg->bellovl_t) {
+ term->beephead->ticks < ticks - term->cfg.bellovl_t) {
struct beeptime *tmp = term->beephead;
term->beephead = tmp->next;
sfree(tmp);
term->nbeeps--;
}
- if (term->cfg->bellovl && term->beep_overloaded &&
- ticks - term->lastbeep >= (unsigned)term->cfg->bellovl_s) {
+ if (term->cfg.bellovl && term->beep_overloaded &&
+ ticks - term->lastbeep >= (unsigned)term->cfg.bellovl_s) {
/*
* If we're currently overloaded and the
* last beep was more than s seconds ago,
* leave overload mode.
*/
term->beep_overloaded = FALSE;
- } else if (term->cfg->bellovl && !term->beep_overloaded &&
- term->nbeeps >= term->cfg->bellovl_n) {
+ } else if (term->cfg.bellovl && !term->beep_overloaded &&
+ term->nbeeps >= term->cfg.bellovl_n) {
/*
* Now, if we have n or more beeps
* remaining in the queue, go into overload
/*
* Perform an actual beep if we're not overloaded.
*/
- if (!term->cfg->bellovl || !term->beep_overloaded) {
- beep(term->frontend, term->cfg->beep);
- if (term->cfg->beep == BELL_VISUAL) {
+ if (!term->cfg.bellovl || !term->beep_overloaded) {
+ beep(term->frontend, term->cfg.beep);
+ if (term->cfg.beep == BELL_VISUAL) {
term->in_vbell = TRUE;
term->vbell_startpoint = ticks;
term_update(term);
}
}
- term->disptop = 0;
+ term->seen_disp_event = TRUE;
}
break;
- case '\b':
+ case '\b': /* BS: Back space */
if (term->curs.x == 0 &&
(term->curs.y == 0 || term->wrap == 0))
/* do nothing */ ;
fix_cpos;
term->seen_disp_event = TRUE;
break;
- case '\016':
+ case '\016': /* LS1: Locking-shift one */
compatibility(VT100);
term->cset = 1;
break;
- case '\017':
+ case '\017': /* LS0: Locking-shift zero */
compatibility(VT100);
term->cset = 0;
break;
- case '\033':
+ case '\033': /* ESC: Escape */
if (term->vt52_mode)
term->termstate = VT52_ESC;
else {
term->esc_query = FALSE;
}
break;
- case '\015':
+ case '\015': /* CR: Carriage return */
term->curs.x = 0;
term->wrapnext = FALSE;
fix_cpos;
if (term->logctx)
logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);
break;
- case '\014':
+ case '\014': /* FF: Form feed */
if (has_compat(SCOANSI)) {
move(term, 0, 0, 0);
erase_lots(term, FALSE, FALSE, TRUE);
term->seen_disp_event = 1;
break;
}
- case '\013':
+ case '\013': /* VT: Line tabulation */
compatibility(VT100);
- case '\012':
+ case '\012': /* LF: Line feed */
if (term->curs.y == term->marg_b)
scroll(term, term->marg_t, term->marg_b, 1, TRUE);
else if (term->curs.y < term->rows - 1)
term->curs.y++;
- if (term->cfg->lfhascr)
+ if (term->cfg.lfhascr)
term->curs.x = 0;
fix_cpos;
term->wrapnext = FALSE;
if (term->logctx)
logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);
break;
- case '\t':
+ case '\t': /* HT: Character tabulation */
{
pos old_curs = term->curs;
unsigned long *ldata = lineptr(term->curs.y);
width = wcwidth((wchar_t) c);
switch (width) {
case 2:
- *term->cpos++ = c | term->curr_attr;
- if (++term->curs.x == term->cols) {
- *term->cpos |= LATTR_WRAPPED;
+ /*
+ * If we're about to display a double-width
+ * character starting in the rightmost
+ * column, then we do something special
+ * instead. We must print a space in the
+ * last column of the screen, then wrap;
+ * and we also set LATTR_WRAPPED2 which
+ * instructs subsequent cut-and-pasting not
+ * only to splice this line to the one
+ * after it, but to ignore the space in the
+ * last character position as well.
+ * (Because what was actually output to the
+ * terminal was presumably just a sequence
+ * of CJK characters, and we don't want a
+ * space to be pasted in the middle of
+ * those just because they had the
+ * misfortune to start in the wrong parity
+ * column. xterm concurs.)
+ */
+ check_boundary(term, term->curs.x, term->curs.y);
+ check_boundary(term, term->curs.x+2, term->curs.y);
+ if (term->curs.x == term->cols-1) {
+ *term->cpos++ = ATTR_ASCII | ' ' | term->curr_attr;
+ *term->cpos |= LATTR_WRAPPED | LATTR_WRAPPED2;
if (term->curs.y == term->marg_b)
scroll(term, term->marg_t, term->marg_b,
1, TRUE);
term->curs.y++;
term->curs.x = 0;
fix_cpos;
+ /* Now we must check_boundary again, of course. */
+ check_boundary(term, term->curs.x, term->curs.y);
+ check_boundary(term, term->curs.x+2, term->curs.y);
}
+ *term->cpos++ = c | term->curr_attr;
*term->cpos++ = UCSWIDE | term->curr_attr;
+ term->curs.x++;
break;
case 1:
+ check_boundary(term, term->curs.x, term->curs.y);
+ check_boundary(term, term->curs.x+1, term->curs.y);
*term->cpos++ = c | term->curr_attr;
break;
default:
}
term->termstate = TOPLEVEL;
switch (ANSI(c, term->esc_query)) {
- case '[': /* enter CSI mode */
+ case '[': /* enter CSI mode */
term->termstate = SEEN_CSI;
term->esc_nargs = 1;
term->esc_args[0] = ARG_DEFAULT;
term->esc_query = FALSE;
break;
- case ']': /* xterm escape sequences */
+ case ']': /* OSC: xterm escape sequences */
/* Compatibility is nasty here, xterm, linux, decterm yuk! */
compatibility(OTHER);
term->termstate = SEEN_OSC;
term->esc_args[0] = 0;
break;
- case '7': /* save cursor */
+ case '7': /* DECSC: save cursor */
compatibility(VT100);
save_cursor(term, TRUE);
break;
- case '8': /* restore cursor */
+ case '8': /* DECRC: restore cursor */
compatibility(VT100);
save_cursor(term, FALSE);
term->seen_disp_event = TRUE;
break;
- case '=':
+ case '=': /* DECKPAM: Keypad application mode */
compatibility(VT100);
term->app_keypad_keys = TRUE;
break;
- case '>':
+ case '>': /* DECKPNM: Keypad numeric mode */
compatibility(VT100);
term->app_keypad_keys = FALSE;
break;
- case 'D': /* exactly equivalent to LF */
+ case 'D': /* IND: exactly equivalent to LF */
compatibility(VT100);
if (term->curs.y == term->marg_b)
scroll(term, term->marg_t, term->marg_b, 1, TRUE);
term->wrapnext = FALSE;
term->seen_disp_event = TRUE;
break;
- case 'E': /* exactly equivalent to CR-LF */
+ case 'E': /* NEL: exactly equivalent to CR-LF */
compatibility(VT100);
term->curs.x = 0;
if (term->curs.y == term->marg_b)
term->wrapnext = FALSE;
term->seen_disp_event = TRUE;
break;
- case 'M': /* reverse index - backwards LF */
+ case 'M': /* RI: reverse index - backwards LF */
compatibility(VT100);
if (term->curs.y == term->marg_t)
scroll(term, term->marg_t, term->marg_b, -1, TRUE);
term->wrapnext = FALSE;
term->seen_disp_event = TRUE;
break;
- case 'Z': /* terminal type query */
+ case 'Z': /* DECID: terminal type query */
compatibility(VT100);
if (term->ldisc)
ldisc_send(term->ldisc, term->id_string,
strlen(term->id_string), 0);
break;
- case 'c': /* restore power-on settings */
+ case 'c': /* RIS: restore power-on settings */
compatibility(VT100);
power_on(term);
if (term->ldisc) /* cause ldisc to notice changes */
ldisc_send(term->ldisc, NULL, 0, 0);
if (term->reset_132) {
- if (!term->cfg->no_remote_resize)
+ if (!term->cfg.no_remote_resize)
request_resize(term->frontend, 80, term->rows);
term->reset_132 = 0;
}
term->disptop = 0;
term->seen_disp_event = TRUE;
break;
- case 'H': /* set a tab */
+ case 'H': /* HTS: set a tab */
compatibility(VT100);
term->tabs[term->curs.x] = TRUE;
break;
- case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
+ case ANSI('8', '#'): /* DECALN: fills screen with Es :-) */
compatibility(VT100);
{
unsigned long *ldata;
unsigned long nlattr;
unsigned long *ldata;
switch (ANSI(c, term->esc_query)) {
- case ANSI('3', '#'):
+ case ANSI('3', '#'): /* DECDHL: 2*height, top */
nlattr = LATTR_TOP;
break;
- case ANSI('4', '#'):
+ case ANSI('4', '#'): /* DECDHL: 2*height, bottom */
nlattr = LATTR_BOT;
break;
- case ANSI('5', '#'):
+ case ANSI('5', '#'): /* DECSWL: normal */
nlattr = LATTR_NORM;
break;
- default: /* spiritually case ANSI('6', '#'): */
+ default: /* case ANSI('6', '#'): DECDWL: 2*width */
nlattr = LATTR_WIDE;
break;
}
ldata[term->cols] |= nlattr;
}
break;
-
+ /* GZD4: G0 designate 94-set */
case ANSI('A', '('):
compatibility(VT100);
- if (!term->cfg->no_remote_charset)
+ if (!term->cfg.no_remote_charset)
term->cset_attr[0] = ATTR_GBCHR;
break;
case ANSI('B', '('):
compatibility(VT100);
- if (!term->cfg->no_remote_charset)
+ if (!term->cfg.no_remote_charset)
term->cset_attr[0] = ATTR_ASCII;
break;
case ANSI('0', '('):
compatibility(VT100);
- if (!term->cfg->no_remote_charset)
+ if (!term->cfg.no_remote_charset)
term->cset_attr[0] = ATTR_LINEDRW;
break;
case ANSI('U', '('):
compatibility(OTHER);
- if (!term->cfg->no_remote_charset)
+ if (!term->cfg.no_remote_charset)
term->cset_attr[0] = ATTR_SCOACS;
break;
-
+ /* G1D4: G1-designate 94-set */
case ANSI('A', ')'):
compatibility(VT100);
- if (!term->cfg->no_remote_charset)
+ if (!term->cfg.no_remote_charset)
term->cset_attr[1] = ATTR_GBCHR;
break;
case ANSI('B', ')'):
compatibility(VT100);
- if (!term->cfg->no_remote_charset)
+ if (!term->cfg.no_remote_charset)
term->cset_attr[1] = ATTR_ASCII;
break;
case ANSI('0', ')'):
compatibility(VT100);
- if (!term->cfg->no_remote_charset)
+ if (!term->cfg.no_remote_charset)
term->cset_attr[1] = ATTR_LINEDRW;
break;
case ANSI('U', ')'):
compatibility(OTHER);
- if (!term->cfg->no_remote_charset)
+ if (!term->cfg.no_remote_charset)
term->cset_attr[1] = ATTR_SCOACS;
break;
-
+ /* DOCS: Designate other coding system */
case ANSI('8', '%'): /* Old Linux code */
case ANSI('G', '%'):
compatibility(OTHER);
- if (!term->cfg->no_remote_charset)
+ if (!term->cfg.no_remote_charset)
term->utf = 1;
break;
case ANSI('@', '%'):
compatibility(OTHER);
- if (!term->cfg->no_remote_charset)
+ if (!term->cfg.no_remote_charset)
term->utf = 0;
break;
}
term->termstate = SEEN_CSI;
} else
switch (ANSI(c, term->esc_query)) {
- case 'A': /* move up N lines */
+ case 'A': /* CUU: move up N lines */
move(term, term->curs.x,
term->curs.y - def(term->esc_args[0], 1), 1);
term->seen_disp_event = TRUE;
break;
- case 'e': /* move down N lines */
+ case 'e': /* VPR: move down N lines */
compatibility(ANSI);
/* FALLTHROUGH */
- case 'B':
+ case 'B': /* CUD: Cursor down */
move(term, term->curs.x,
term->curs.y + def(term->esc_args[0], 1), 1);
term->seen_disp_event = TRUE;
break;
- case ANSI('c', '>'): /* report xterm version */
+ case ANSI('c', '>'): /* DA: report xterm version */
compatibility(OTHER);
/* this reports xterm version 136 so that VIM can
use the drag messages from the mouse reporting */
if (term->ldisc)
ldisc_send(term->ldisc, "\033[>0;136;0c", 11, 0);
break;
- case 'a': /* move right N cols */
+ case 'a': /* HPR: move right N cols */
compatibility(ANSI);
/* FALLTHROUGH */
- case 'C':
+ case 'C': /* CUF: Cursor right */
move(term, term->curs.x + def(term->esc_args[0], 1),
term->curs.y, 1);
term->seen_disp_event = TRUE;
break;
- case 'D': /* move left N cols */
+ case 'D': /* CUB: move left N cols */
move(term, term->curs.x - def(term->esc_args[0], 1),
term->curs.y, 1);
term->seen_disp_event = TRUE;
break;
- case 'E': /* move down N lines and CR */
+ case 'E': /* CNL: move down N lines and CR */
compatibility(ANSI);
move(term, 0,
term->curs.y + def(term->esc_args[0], 1), 1);
term->seen_disp_event = TRUE;
break;
- case 'F': /* move up N lines and CR */
+ case 'F': /* CPL: move up N lines and CR */
compatibility(ANSI);
move(term, 0,
term->curs.y - def(term->esc_args[0], 1), 1);
term->seen_disp_event = TRUE;
break;
- case 'G':
- case '`': /* set horizontal posn */
+ case 'G': /* CHA */
+ case '`': /* HPA: set horizontal posn */
compatibility(ANSI);
move(term, def(term->esc_args[0], 1) - 1,
term->curs.y, 0);
term->seen_disp_event = TRUE;
break;
- case 'd': /* set vertical posn */
+ case 'd': /* VPA: set vertical posn */
compatibility(ANSI);
move(term, term->curs.x,
((term->dec_om ? term->marg_t : 0) +
(term->dec_om ? 2 : 0));
term->seen_disp_event = TRUE;
break;
- case 'H':
- case 'f': /* set horz and vert posns at once */
+ case 'H': /* CUP */
+ case 'f': /* HVP: set horz and vert posns at once */
if (term->esc_nargs < 2)
term->esc_args[1] = ARG_DEFAULT;
move(term, def(term->esc_args[1], 1) - 1,
(term->dec_om ? 2 : 0));
term->seen_disp_event = TRUE;
break;
- case 'J': /* erase screen or parts of it */
+ case 'J': /* ED: erase screen or parts of it */
{
unsigned int i = def(term->esc_args[0], 0) + 1;
if (i > 3)
term->disptop = 0;
term->seen_disp_event = TRUE;
break;
- case 'K': /* erase line or parts of it */
+ case 'K': /* EL: erase line or parts of it */
{
unsigned int i = def(term->esc_args[0], 0) + 1;
if (i > 3)
}
term->seen_disp_event = TRUE;
break;
- case 'L': /* insert lines */
+ case 'L': /* IL: insert lines */
compatibility(VT102);
if (term->curs.y <= term->marg_b)
scroll(term, term->curs.y, term->marg_b,
fix_cpos;
term->seen_disp_event = TRUE;
break;
- case 'M': /* delete lines */
+ case 'M': /* DL: delete lines */
compatibility(VT102);
if (term->curs.y <= term->marg_b)
scroll(term, term->curs.y, term->marg_b,
fix_cpos;
term->seen_disp_event = TRUE;
break;
- case '@': /* insert chars */
+ case '@': /* ICH: insert chars */
/* XXX VTTEST says this is vt220, vt510 manual says vt102 */
compatibility(VT102);
insch(term, def(term->esc_args[0], 1));
term->seen_disp_event = TRUE;
break;
- case 'P': /* delete chars */
+ case 'P': /* DCH: delete chars */
compatibility(VT102);
insch(term, -def(term->esc_args[0], 1));
term->seen_disp_event = TRUE;
break;
- case 'c': /* terminal type query */
+ case 'c': /* DA: terminal type query */
compatibility(VT100);
/* This is the response for a VT102 */
if (term->ldisc)
ldisc_send(term->ldisc, term->id_string,
strlen(term->id_string), 0);
break;
- case 'n': /* cursor position query */
+ case 'n': /* DSR: cursor position query */
if (term->ldisc) {
if (term->esc_args[0] == 6) {
char buf[32];
}
}
break;
- case 'h': /* toggle modes to high */
+ case 'h': /* SM: toggle modes to high */
case ANSI_QUE('h'):
compatibility(VT100);
{
term->esc_query, TRUE);
}
break;
- case 'i':
+ case 'i': /* MC: Media copy */
case ANSI_QUE('i'):
compatibility(VT100);
{
if (term->esc_nargs != 1) break;
- if (term->esc_args[0] == 5 && *term->cfg->printer) {
+ if (term->esc_args[0] == 5 && *term->cfg.printer) {
term->printing = TRUE;
term->only_printing = !term->esc_query;
term->print_state = 0;
}
}
break;
- case 'l': /* toggle modes to low */
+ case 'l': /* RM: toggle modes to low */
case ANSI_QUE('l'):
compatibility(VT100);
{
term->esc_query, FALSE);
}
break;
- case 'g': /* clear tabs */
+ case 'g': /* TBC: clear tabs */
compatibility(VT100);
if (term->esc_nargs == 1) {
if (term->esc_args[0] == 0) {
}
}
break;
- case 'r': /* set scroll margins */
+ case 'r': /* DECSTBM: set scroll margins */
compatibility(VT100);
if (term->esc_nargs <= 2) {
int top, bot;
}
}
break;
- case 'm': /* set graphics rendition */
+ case 'm': /* SGR: set graphics rendition */
{
/*
* A VT100 without the AVO only had one
for (i = 0; i < term->esc_nargs; i++) {
switch (def(term->esc_args[i], 0)) {
case 0: /* restore defaults */
- term->curr_attr = ATTR_DEFAULT;
+ term->curr_attr = term->default_attr;
break;
case 1: /* enable bold */
compatibility(VT100AVO);
compatibility(VT100AVO);
term->curr_attr |= ATTR_BLINK;
break;
+ case 6: /* SCO light bkgrd */
+ compatibility(SCOANSI);
+ term->blink_is_real = FALSE;
+ term->curr_attr |= ATTR_BLINK;
+ break;
case 7: /* enable reverse video */
term->curr_attr |= ATTR_REVERSE;
break;
case 10: /* SCO acs off */
compatibility(SCOANSI);
- if (term->cfg->no_remote_charset) break;
+ if (term->cfg.no_remote_charset) break;
term->sco_acs = 0; break;
case 11: /* SCO acs on */
compatibility(SCOANSI);
- if (term->cfg->no_remote_charset) break;
+ if (term->cfg.no_remote_charset) break;
term->sco_acs = 1; break;
case 12: /* SCO acs on, |0x80 */
compatibility(SCOANSI);
- if (term->cfg->no_remote_charset) break;
+ if (term->cfg.no_remote_charset) break;
term->sco_acs = 2; break;
case 22: /* disable bold */
compatibility2(OTHER, VT220);
term->curr_attr |=
(term->esc_args[i] - 30)<<ATTR_FGSHIFT;
break;
+ case 90:
+ case 91:
+ case 92:
+ case 93:
+ case 94:
+ case 95:
+ case 96:
+ case 97:
+ /* xterm-style bright foreground */
+ term->curr_attr &= ~ATTR_FGMASK;
+ term->curr_attr |=
+ ((term->esc_args[i] - 90 + 16)
+ << ATTR_FGSHIFT);
+ break;
case 39: /* default-foreground */
term->curr_attr &= ~ATTR_FGMASK;
term->curr_attr |= ATTR_DEFFG;
term->curr_attr |=
(term->esc_args[i] - 40)<<ATTR_BGSHIFT;
break;
+ case 100:
+ case 101:
+ case 102:
+ case 103:
+ case 104:
+ case 105:
+ case 106:
+ case 107:
+ /* xterm-style bright background */
+ term->curr_attr &= ~ATTR_BGMASK;
+ term->curr_attr |=
+ ((term->esc_args[i] - 100 + 16)
+ << ATTR_BGSHIFT);
+ break;
case 49: /* default-background */
term->curr_attr &= ~ATTR_BGMASK;
term->curr_attr |= ATTR_DEFBG;
save_cursor(term, FALSE);
term->seen_disp_event = TRUE;
break;
- case 't': /* set page size - ie window height */
+ case 't': /* DECSLPP: set page size - ie window height */
/*
* VT340/VT420 sequence DECSLPP, DEC only allows values
* 24/25/36/48/72/144 other emulators (eg dtterm) use
&& (term->esc_args[0] < 1 ||
term->esc_args[0] >= 24)) {
compatibility(VT340TEXT);
- if (!term->cfg->no_remote_resize)
+ if (!term->cfg.no_remote_resize)
request_resize(term->frontend, term->cols,
def(term->esc_args[0], 24));
deselect(term);
break;
case 3:
if (term->esc_nargs >= 3) {
- if (!term->cfg->no_remote_resize)
+ if (!term->cfg.no_remote_resize)
move_window(term->frontend,
def(term->esc_args[1], 0),
def(term->esc_args[2], 0));
break;
case 8:
if (term->esc_nargs >= 3) {
- if (!term->cfg->no_remote_resize)
+ if (!term->cfg.no_remote_resize)
request_resize(term->frontend,
- def(term->esc_args[2], term->cfg->width),
- def(term->esc_args[1], term->cfg->height));
+ def(term->esc_args[2], term->cfg.width),
+ def(term->esc_args[1], term->cfg.height));
}
break;
case 9:
*/
break;
case 20:
- if (term->ldisc) {
+ if (term->ldisc &&
+ !term->cfg.no_remote_qtitle) {
p = get_window_title(term->frontend, TRUE);
len = strlen(p);
ldisc_send(term->ldisc, "\033]L", 3, 0);
}
break;
case 21:
- if (term->ldisc) {
+ if (term->ldisc &&
+ !term->cfg.no_remote_qtitle) {
p = get_window_title(term->frontend,FALSE);
len = strlen(p);
ldisc_send(term->ldisc, "\033]l", 3, 0);
}
}
break;
- case 'S':
+ case 'S': /* SU: Scroll up */
compatibility(SCOANSI);
scroll(term, term->marg_t, term->marg_b,
def(term->esc_args[0], 1), TRUE);
term->wrapnext = FALSE;
term->seen_disp_event = TRUE;
break;
- case 'T':
+ case 'T': /* SD: Scroll down */
compatibility(SCOANSI);
scroll(term, term->marg_t, term->marg_b,
-def(term->esc_args[0], 1), TRUE);
term->wrapnext = FALSE;
term->seen_disp_event = TRUE;
break;
- case ANSI('|', '*'):
- /* VT420 sequence DECSNLS
+ case ANSI('|', '*'): /* DECSNLS */
+ /*
* Set number of lines on screen
- * VT420 uses VGA like hardware and can support any size in
- * reasonable range (24..49 AIUI) with no default specified.
+ * VT420 uses VGA like hardware and can
+ * support any size in reasonable range
+ * (24..49 AIUI) with no default specified.
*/
compatibility(VT420);
if (term->esc_nargs == 1 && term->esc_args[0] > 0) {
- if (!term->cfg->no_remote_resize)
+ if (!term->cfg.no_remote_resize)
request_resize(term->frontend, term->cols,
def(term->esc_args[0],
- term->cfg->height));
+ term->cfg.height));
deselect(term);
}
break;
- case ANSI('|', '$'):
- /* VT340/VT420 sequence DECSCPP
+ case ANSI('|', '$'): /* DECSCPP */
+ /*
* Set number of columns per page
- * Docs imply range is only 80 or 132, but I'll allow any.
+ * Docs imply range is only 80 or 132, but
+ * I'll allow any.
*/
compatibility(VT340TEXT);
if (term->esc_nargs <= 1) {
- if (!term->cfg->no_remote_resize)
+ if (!term->cfg.no_remote_resize)
request_resize(term->frontend,
def(term->esc_args[0],
- term->cfg->width), term->rows);
+ term->cfg.width), term->rows);
deselect(term);
}
break;
- case 'X': /* write N spaces w/o moving cursor */
- /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
+ case 'X': /* ECH: write N spaces w/o moving cursor */
+ /* XXX VTTEST says this is vt220, vt510 manual
+ * says vt100 */
compatibility(ANSIMIN);
{
int n = def(term->esc_args[0], 1);
n = term->cols - term->curs.x;
cursplus = term->curs;
cursplus.x += n;
+ check_boundary(term, term->curs.x, term->curs.y);
+ check_boundary(term, term->curs.x+n, term->curs.y);
check_selection(term, term->curs, cursplus);
while (n--)
*p++ = term->erase_char;
term->seen_disp_event = TRUE;
}
break;
- case 'x': /* report terminal characteristics */
+ case 'x': /* DECREQTPARM: report terminal characteristics */
compatibility(VT100);
if (term->ldisc) {
char buf[32];
}
}
break;
- case 'Z': /* BackTab for xterm */
+ case 'Z': /* CBT: BackTab for xterm */
compatibility(OTHER);
{
int i = def(term->esc_args[0], 1);
check_selection(term, old_curs, term->curs);
}
break;
+ case ANSI('c', '='): /* Hide or Show Cursor */
+ compatibility(SCOANSI);
+ switch(term->esc_args[0]) {
+ case 0: /* hide cursor */
+ term->cursor_on = FALSE;
+ break;
+ case 1: /* restore cursor */
+ term->big_cursor = FALSE;
+ term->cursor_on = TRUE;
+ break;
+ case 2: /* block cursor */
+ term->big_cursor = TRUE;
+ term->cursor_on = TRUE;
+ break;
+ }
+ break;
+ case ANSI('C', '='):
+ /*
+ * set cursor start on scanline esc_args[0] and
+ * end on scanline esc_args[1].If you set
+ * the bottom scan line to a value less than
+ * the top scan line, the cursor will disappear.
+ */
+ compatibility(SCOANSI);
+ if (term->esc_nargs >= 2) {
+ if (term->esc_args[0] > term->esc_args[1])
+ term->cursor_on = FALSE;
+ else
+ term->cursor_on = TRUE;
+ }
+ break;
+ case ANSI('D', '='):
+ compatibility(SCOANSI);
+ term->blink_is_real = FALSE;
+ if (term->esc_args[0]>=1)
+ term->curr_attr |= ATTR_BLINK;
+ else
+ term->curr_attr &= ~ATTR_BLINK;
+ break;
+ case ANSI('E', '='):
+ compatibility(SCOANSI);
+ term->blink_is_real = (term->esc_args[0] >= 1);
+ break;
+ case ANSI('F', '='): /* set normal foreground */
+ compatibility(SCOANSI);
+ if (term->esc_args[0] >= 0 && term->esc_args[0] < 16) {
+ long colour =
+ (sco2ansicolour[term->esc_args[0] & 0x7] |
+ ((term->esc_args[0] & 0x8) << 1)) <<
+ ATTR_FGSHIFT;
+ term->curr_attr &= ~ATTR_FGMASK;
+ term->curr_attr |= colour;
+ term->default_attr &= ~ATTR_FGMASK;
+ term->default_attr |= colour;
+ }
+ break;
+ case ANSI('G', '='): /* set normal background */
+ compatibility(SCOANSI);
+ if (term->esc_args[0] >= 0 && term->esc_args[0] < 16) {
+ long colour =
+ (sco2ansicolour[term->esc_args[0] & 0x7] |
+ ((term->esc_args[0] & 0x8) << 1)) <<
+ ATTR_BGSHIFT;
+ term->curr_attr &= ~ATTR_BGMASK;
+ term->curr_attr |= colour;
+ term->default_attr &= ~ATTR_BGMASK;
+ term->default_attr |= colour;
+ }
+ break;
case ANSI('L', '='):
- compatibility(OTHER);
+ compatibility(SCOANSI);
term->use_bce = (term->esc_args[0] <= 0);
term->erase_char = ERASE_CHAR;
if (term->use_bce)
(term->curr_attr &
(ATTR_FGMASK | ATTR_BGMASK)));
break;
- case ANSI('E', '='):
- compatibility(OTHER);
- term->blink_is_real = (term->esc_args[0] >= 1);
- break;
- case ANSI('p', '"'):
+ case ANSI('p', '"'): /* DECSCL: set compat level */
/*
* Allow the host to make this emulator a
* 'perfect' VT102. This first appeared in
* Well we should do a soft reset at this point ...
*/
if (!has_compat(VT420) && has_compat(VT100)) {
- if (!term->cfg->no_remote_resize) {
+ if (!term->cfg.no_remote_resize) {
if (term->reset_132)
request_resize(132, 24);
else
* emulation.
*/
term->vt52_mode = FALSE;
- term->blink_is_real = term->cfg->blinktext;
+ term->blink_is_real = term->cfg.blinktext;
break;
#if 0
case '^':
*/
static void do_paint(Terminal *term, Context ctx, int may_optimise)
{
- int i, j, our_curs_y;
+ int i, j, our_curs_y, our_curs_x;
unsigned long rv, cursor;
pos scrpos;
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.
/* Has the cursor position or type changed ? */
if (term->cursor_on) {
if (term->has_focus) {
- if (term->blinker || !term->cfg->blink_cur)
+ if (term->blinker || !term->cfg.blink_cur)
cursor = TATTR_ACTCURS;
else
cursor = 0;
} else
cursor = 0;
our_curs_y = term->curs.y - term->disptop;
+ {
+ /*
+ * Adjust the cursor position in the case where it's
+ * resting on the right-hand half of a CJK wide character.
+ * xterm's behaviour here, which seems adequate to me, is
+ * to display the cursor covering the _whole_ character,
+ * exactly as if it were one space to the left.
+ */
+ unsigned long *ldata = lineptr(term->curs.y);
+ our_curs_x = term->curs.x;
+ if (our_curs_x > 0 &&
+ (ldata[our_curs_x] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
+ our_curs_x--;
+ }
if (term->dispcurs && (term->curstype != cursor ||
term->dispcurs !=
term->disptext + our_curs_y * (term->cols + 1) +
- term->curs.x)) {
+ our_curs_x)) {
if (term->dispcurs > term->disptext &&
- (*term->dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
+ (*term->dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
term->dispcurs[-1] |= ATTR_INVALID;
if ( (term->dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
term->dispcurs[1] |= ATTR_INVALID;
}
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;
tattr = (*d & (ATTR_MASK ^ CSET_MASK));
switch (tchar & CSET_MASK) {
case ATTR_ASCII:
- tchar = unitab_line[tchar & 0xFF];
+ tchar = term->ucsdata->unitab_line[tchar & 0xFF];
break;
case ATTR_LINEDRW:
- tchar = unitab_xterm[tchar & 0xFF];
+ tchar = term->ucsdata->unitab_xterm[tchar & 0xFF];
break;
case ATTR_SCOACS:
- tchar = unitab_scoacs[tchar&0xFF];
+ tchar = term->ucsdata->unitab_scoacs[tchar&0xFF];
break;
}
tattr |= (tchar & CSET_MASK);
/* 'Real' blinking ? */
if (term->blink_is_real && (tattr & ATTR_BLINK)) {
if (term->has_focus && term->tblinker) {
- tchar = ' ';
- tattr &= ~CSET_MASK;
- tattr |= ATTR_ACP;
+ tchar = term->ucsdata->unitab_line[(unsigned char)' '];
}
tattr &= ~ATTR_BLINK;
}
tattr |= ATTR_NARROW;
/* Cursor here ? Save the 'background' */
- if (i == our_curs_y && j == term->curs.x) {
+ if (i == our_curs_y && j == our_curs_x) {
cursor_background = tattr | tchar;
term->dispcurs = term->disptext + idx;
}
if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
&& tchar <= 0xBD) break_run = TRUE;
- if (!dbcs_screenfont && !dirty_line) {
+ if (!term->ucsdata->dbcs_screenfont && !dirty_line) {
if ((tchar | tattr) == term->disptext[idx])
break_run = TRUE;
else if (!dirty_run && ccount == 1)
start = j;
ccount = 0;
attr = tattr;
- if (dbcs_screenfont)
+ if (term->ucsdata->dbcs_screenfont)
last_run_dirty = dirty_run;
dirty_run = dirty_line;
}
if (++j < term->cols) {
idx++;
d++;
- /* Cursor is here ? Ouch! */
- if (i == our_curs_y && j == term->curs.x) {
- cursor_background = *d;
- term->dispcurs = term->disptext + idx;
- }
+ /*
+ * By construction above, the cursor should not
+ * be on the right-hand half of this character.
+ * Ever.
+ */
+ assert(!(i == our_curs_y && j == our_curs_x));
if (term->disptext[idx] != *d)
dirty_run = TRUE;
term->disptext[idx] = *d;
if (i == our_curs_y && (term->curstype != cursor || updated_line)) {
ch[0] = (char) (cursor_background & CHAR_MASK);
attr = (cursor_background & ATTR_MASK) | cursor;
- do_cursor(ctx, term->curs.x, i, ch, 1, attr, lattr);
+ do_cursor(ctx, our_curs_x, i, ch, 1, attr, lattr);
term->curstype = cursor;
}
}
*/
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;
term_update(term);
}
-static void clipme(Terminal *term, pos top, pos bottom, int rect)
+static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel)
{
wchar_t *workbuf;
wchar_t *wbptr; /* where next char goes within workbuf */
int buflen; /* amount of memory allocated to workbuf */
buflen = 5120; /* Default size */
- workbuf = smalloc(buflen * sizeof(wchar_t));
+ workbuf = snewn(buflen, wchar_t);
wbptr = workbuf; /* start filling here */
old_top_x = top.x; /* needed for rect==1 */
decpos(nlpos);
if (poslt(nlpos, bottom))
nl = TRUE;
+ } else if (ldata[term->cols] & LATTR_WRAPPED2) {
+ /* Ignore the last char on the line in a WRAPPED2 line. */
+ decpos(nlpos);
}
/*
switch (uc & CSET_MASK) {
case ATTR_LINEDRW:
- if (!term->cfg->rawcnp) {
- uc = unitab_xterm[uc & 0xFF];
+ if (!term->cfg.rawcnp) {
+ uc = term->ucsdata->unitab_xterm[uc & 0xFF];
break;
}
case ATTR_ASCII:
- uc = unitab_line[uc & 0xFF];
+ uc = term->ucsdata->unitab_line[uc & 0xFF];
break;
case ATTR_SCOACS:
- uc = unitab_scoacs[uc&0xFF];
+ uc = term->ucsdata->unitab_scoacs[uc&0xFF];
break;
}
switch (uc & CSET_MASK) {
case ATTR_ACP:
- uc = unitab_font[uc & 0xFF];
+ uc = term->ucsdata->unitab_font[uc & 0xFF];
break;
case ATTR_OEMCP:
- uc = unitab_oemcp[uc & 0xFF];
+ uc = term->ucsdata->unitab_oemcp[uc & 0xFF];
break;
}
char buf[4];
WCHAR wbuf[4];
int rv;
- if (is_dbcs_leadbyte(font_codepage, (BYTE) c)) {
+ if (is_dbcs_leadbyte(term->ucsdata->font_codepage, (BYTE) c)) {
buf[0] = c;
buf[1] = (char) (0xFF & ldata[top.x + 1]);
- rv = mb_to_wc(font_codepage, 0, buf, 2, wbuf, 4);
+ rv = mb_to_wc(term->ucsdata->font_codepage, 0, buf, 2, wbuf, 4);
top.x++;
} else {
buf[0] = c;
- rv = mb_to_wc(font_codepage, 0, buf, 1, wbuf, 4);
+ rv = mb_to_wc(term->ucsdata->font_codepage, 0, buf, 1, wbuf, 4);
}
if (rv > 0) {
for (p = cbuf; *p; p++) {
/* Enough overhead for trailing NL and nul */
if (wblen >= buflen - 16) {
- workbuf =
- srealloc(workbuf,
- sizeof(wchar_t) * (buflen += 100));
+ buflen += 100;
+ workbuf = sresize(workbuf, buflen, wchar_t);
wbptr = workbuf + wblen;
}
wblen++;
wblen++;
*wbptr++ = 0;
#endif
- write_clip(term->frontend, workbuf, wblen, FALSE); /* transfer to clipbd */
+ write_clip(term->frontend, workbuf, wblen, desel); /* transfer to clipbd */
if (buflen > 0) /* indicates we allocated this buffer */
sfree(workbuf);
}
void term_copyall(Terminal *term)
{
pos top;
- top.y = -count234(term->scrollback);
+ pos bottom;
+ tree234 *screen = term->screen;
+ top.y = -sblines(term);
top.x = 0;
- clipme(term, top, term->curs, 0);
+ bottom.y = find_last_nonempty_line(term, screen);
+ bottom.x = term->cols;
+ clipme(term, top, bottom, 0, TRUE);
}
/*
switch (uc & CSET_MASK) {
case ATTR_LINEDRW:
- uc = unitab_xterm[uc & 0xFF];
+ uc = term->ucsdata->unitab_xterm[uc & 0xFF];
break;
case ATTR_ASCII:
- uc = unitab_line[uc & 0xFF];
+ uc = term->ucsdata->unitab_line[uc & 0xFF];
break;
case ATTR_SCOACS:
- uc = unitab_scoacs[uc&0xFF];
+ uc = term->ucsdata->unitab_scoacs[uc&0xFF];
break;
}
switch (uc & CSET_MASK) {
case ATTR_ACP:
- uc = unitab_font[uc & 0xFF];
+ uc = term->ucsdata->unitab_font[uc & 0xFF];
break;
case ATTR_OEMCP:
- uc = unitab_oemcp[uc & 0xFF];
+ uc = term->ucsdata->unitab_oemcp[uc & 0xFF];
break;
}
/* For DBCS font's I can't do anything usefull. Even this will sometimes
* fail as there's such a thing as a double width space. :-(
*/
- if (dbcs_screenfont && font_codepage == line_codepage)
+ if (term->ucsdata->dbcs_screenfont &&
+ term->ucsdata->font_codepage == term->ucsdata->line_codepage)
return (uc != ' ');
if (uc < 0x80)
{
unsigned long *ldata;
short wvalue;
- int topy = -count234(term->scrollback);
+ int topy = -sblines(term);
ldata = lineptr(p.y);
* In this mode, the units are maximal runs of characters
* whose `wordness' has the same value.
*/
- wvalue = wordtype(term, ldata[p.x]);
+ wvalue = wordtype(term, UCSGET(ldata, p.x));
if (dir == +1) {
while (1) {
- if (p.x < term->cols-1) {
- if (wordtype(term, ldata[p.x + 1]) == wvalue)
+ int maxcols = (ldata[term->cols] & LATTR_WRAPPED2 ?
+ term->cols-1 : term->cols);
+ if (p.x < maxcols-1) {
+ if (wordtype(term, UCSGET(ldata, p.x + 1)) == wvalue)
p.x++;
else
break;
if (ldata[term->cols] & LATTR_WRAPPED) {
unsigned long *ldata2;
ldata2 = lineptr(p.y+1);
- if (wordtype(term, ldata2[0]) == wvalue) {
+ if (wordtype(term, UCSGET(ldata2, 0)) == wvalue) {
p.x = 0;
p.y++;
ldata = ldata2;
} else {
while (1) {
if (p.x > 0) {
- if (wordtype(term, ldata[p.x - 1]) == wvalue)
+ if (wordtype(term, UCSGET(ldata, p.x - 1)) == wvalue)
p.x--;
else
break;
} else {
unsigned long *ldata2;
+ int maxcols;
if (p.y <= topy)
break;
ldata2 = lineptr(p.y-1);
- if ((ldata2[term->cols] & LATTR_WRAPPED) &&
- wordtype(term, ldata2[term->cols-1]) == wvalue) {
- p.x = term->cols-1;
- p.y--;
- ldata = ldata2;
+ maxcols = (ldata2[term->cols] & LATTR_WRAPPED2 ?
+ term->cols-1 : term->cols);
+ if (ldata2[term->cols] & LATTR_WRAPPED) {
+ if (wordtype(term, UCSGET(ldata2, maxcols-1))
+ == wvalue) {
+ p.x = maxcols-1;
+ p.y--;
+ ldata = ldata2;
+ } else
+ break;
} else
break;
}
if (term->paste_buffer)
sfree(term->paste_buffer);
term->paste_pos = term->paste_hold = term->paste_len = 0;
- term->paste_buffer = smalloc(len * sizeof(wchar_t));
+ term->paste_buffer = snewn(len, wchar_t);
p = q = data;
while (p < data + len) {
get_clip(term->frontend, NULL, NULL);
}
-void term_mouse(Terminal *term, Mouse_Button b, Mouse_Action a, int x, int y,
- int shift, int ctrl, int alt)
+void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked,
+ Mouse_Action a, int x, int y, int shift, int ctrl, int alt)
{
pos selpoint;
unsigned long *ldata;
int raw_mouse = (term->xterm_mouse &&
- !term->cfg->no_mouse_rep &&
- !(term->cfg->mouse_override && shift));
+ !term->cfg.no_mouse_rep &&
+ !(term->cfg.mouse_override && shift));
int default_seltype;
if (y < 0) {
if (term->ldisc) {
- switch (b) {
+ switch (braw) {
case MBT_LEFT:
encstate = 0x20; /* left button down */
break;
term->mouse_is_down = 0;
break;
case MA_CLICK:
- if (term->mouse_is_down == b)
+ if (term->mouse_is_down == braw)
return;
- term->mouse_is_down = b;
+ term->mouse_is_down = braw;
break;
default: break; /* placate gcc warning about enum use */
}
return;
}
- b = translate_button(term->frontend, b);
-
/*
* Set the selection type (rectangular or normal) at the start
* of a selection attempt, from the state of Alt.
*/
- if (!alt ^ !term->cfg->rect_select)
+ if (!alt ^ !term->cfg.rect_select)
default_seltype = RECTANGULAR;
else
default_seltype = LEXICOGRAPHIC;
term->seltype = default_seltype;
}
- if (b == MBT_SELECT && a == MA_CLICK) {
+ if (bcooked == MBT_SELECT && a == MA_CLICK) {
deselect(term);
term->selstate = ABOUT_TO;
term->seltype = default_seltype;
term->selanchor = selpoint;
term->selmode = SM_CHAR;
- } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
+ } else if (bcooked == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
deselect(term);
term->selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
term->selstate = DRAGGING;
term->selend = term->selstart;
incpos(term->selend);
sel_spread(term);
- } else if ((b == MBT_SELECT && a == MA_DRAG) ||
- (b == MBT_EXTEND && a != MA_RELEASE)) {
+ } else if ((bcooked == MBT_SELECT && a == MA_DRAG) ||
+ (bcooked == MBT_EXTEND && a != MA_RELEASE)) {
if (term->selstate == ABOUT_TO && poseq(term->selanchor, selpoint))
return;
- if (b == MBT_EXTEND && a != MA_DRAG && term->selstate == SELECTED) {
+ if (bcooked == MBT_EXTEND && a != MA_DRAG &&
+ term->selstate == SELECTED) {
if (term->seltype == LEXICOGRAPHIC) {
/*
* For normal selection, we extend by moving
term->selend.y = max(term->selanchor.y, selpoint.y);
}
sel_spread(term);
- } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
+ } else if ((bcooked == MBT_SELECT || bcooked == MBT_EXTEND) &&
+ a == MA_RELEASE) {
if (term->selstate == DRAGGING) {
/*
* We've completed a selection. We now transfer the
* data to the clipboard.
*/
clipme(term, term->selstart, term->selend,
- (term->seltype == RECTANGULAR));
+ (term->seltype == RECTANGULAR), FALSE);
term->selstate = SELECTED;
} else
term->selstate = NO_SELECTION;
- } else if (b == MBT_PASTE
+ } else if (bcooked == MBT_PASTE
&& (a == MA_CLICK
#if MULTICLICK_ONLY_EVENT
|| a == MA_2CLK || a == MA_3CLK
term_update(term);
}
+void term_key(Terminal *term, Key_Sym keysym, wchar_t *text, size_t tlen,
+ unsigned int modifiers, unsigned int flags)
+{
+ 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", (unsigned)text[i]);
+ fprintf(stderr, "\n");
+#endif
+
+ /* XXX Num Lock */
+ if ((flags & PKF_REPEAT) && term->repeat_off)
+ return;
+
+ /* Currently, Meta always just prefixes everything with ESC. */
+ if (modifiers & PKM_META)
+ prependesc = TRUE;
+ modifiers &= ~PKM_META;
+
+ /*
+ * Alt is only used for Alt+keypad, which isn't supported yet, so
+ * ignore it.
+ */
+ modifiers &= ~PKM_ALT;
+
+ /* Standard local function keys */
+ switch (modifiers & (PKM_SHIFT | PKM_CONTROL)) {
+ case PKM_SHIFT:
+ if (keysym == PK_PAGEUP)
+ /* scroll up one page */;
+ if (keysym == PK_PAGEDOWN)
+ /* scroll down on page */;
+ if (keysym == PK_INSERT)
+ term_do_paste(term);
+ break;
+ case PKM_CONTROL:
+ if (keysym == PK_PAGEUP)
+ /* scroll up one line */;
+ if (keysym == PK_PAGEDOWN)
+ /* scroll down one line */;
+ /* Control-Numlock for app-keypad mode switch */
+ if (keysym == PK_PF1)
+ term->app_keypad_keys ^= 1;
+ break;
+ }
+
+ if (modifiers & PKM_ALT) {
+ /* Alt+F4 (close) */
+ /* Alt+Return (full screen) */
+ /* Alt+Space (system menu) */
+ }
+
+ 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))
+ text[0] &= 0x1f;
+ else {
+ /*
+ * Control-2 should return ^@ (0x00), Control-6 should return
+ * ^^ (0x1E), and Control-Minus should return ^_ (0x1F). Since
+ * the DOS keyboard handling did it, and we have nothing better
+ * to do with the key combo in question, we'll also map
+ * Control-Backquote to ^\ (0x1C).
+ */
+ switch (text[0]) {
+ case ' ': text[0] = 0x00; break;
+ case '-': text[0] = 0x1f; break;
+ case '/': text[0] = 0x1f; break;
+ case '2': text[0] = 0x00; break;
+ case '3': text[0] = 0x1b; break;
+ case '4': text[0] = 0x1c; break;
+ case '5': text[0] = 0x1d; break;
+ case '6': text[0] = 0x1e; break;
+ case '7': text[0] = 0x1f; break;
+ case '8': text[0] = 0x7f; break;
+ case '`': text[0] = 0x1c; break;
+ }
+ }
+ }
+
+ /* Nethack keypad */
+ if (term->cfg.nethack_keypad) {
+ char c = 0;
+ switch (keysym) {
+ case PK_KP1: c = 'b'; break;
+ case PK_KP2: c = 'j'; break;
+ case PK_KP3: c = 'n'; break;
+ case PK_KP4: c = 'h'; break;
+ case PK_KP5: c = '.'; break;
+ case PK_KP6: c = 'l'; break;
+ 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 != '.') {
+ if (modifiers & PKM_CONTROL)
+ c &= 0x1f;
+ else if (modifiers & PKM_SHIFT)
+ c = toupper(c);
+ }
+ *p++ = c;
+ goto done;
+ }
+ }
+
+ /* Numeric Keypad */
+ if (PK_ISKEYPAD(keysym)) {
+ int xkey = 0;
+
+ /*
+ * In VT400 mode, PFn always emits an escape sequence. In
+ * Linux and tilde modes, this only happens in app keypad mode.
+ */
+ if (term->cfg.funky_type == FUNKY_VT400 ||
+ ((term->cfg.funky_type == FUNKY_LINUX ||
+ term->cfg.funky_type == FUNKY_TILDE) &&
+ term->app_keypad_keys && !term->cfg.no_applic_k)) {
+ switch (keysym) {
+ case PK_PF1: xkey = 'P'; break;
+ 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) {
+ switch (keysym) {
+ case PK_KP0: xkey = 'p'; break;
+ case PK_KP1: xkey = 'q'; break;
+ case PK_KP2: xkey = 'r'; break;
+ case PK_KP3: xkey = 's'; break;
+ case PK_KP4: xkey = 't'; break;
+ case PK_KP5: xkey = 'u'; break;
+ case PK_KP6: xkey = 'v'; break;
+ case PK_KP7: xkey = 'w'; break;
+ case PK_KP8: xkey = 'x'; break;
+ 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) {
+ /*
+ * xterm can't see the layout of the keypad, so it has
+ * to rely on the X keysyms returned by the keys.
+ * Hence, we look at the strings here, not the PuTTY
+ * keysyms (which describe the layout).
+ */
+ switch (text[0]) {
+ case '+':
+ if (modifiers & PKM_SHIFT)
+ xkey = 'l';
+ else
+ xkey = 'k';
+ break;
+ case '/': xkey = 'o'; break;
+ case '*': xkey = 'j'; break;
+ case '-': xkey = 'm'; break;
+ }
+ } else {
+ /*
+ * In all other modes, we try to retain the layout of
+ * the DEC keypad in application mode.
+ */
+ switch (keysym) {
+ case PK_KPBIGPLUS:
+ /* This key covers the '-' and ',' keys on a VT220 */
+ if (modifiers & PKM_SHIFT)
+ xkey = 'm'; /* VT220 '-' */
+ else
+ xkey = 'l'; /* VT220 ',' */
+ break;
+ case PK_KPMINUS: xkey = 'm'; break;
+ case PK_KPCOMMA: xkey = 'l'; break;
+ default: break; /* else gcc warns `enum value not used' */
+ }
+ }
+ }
+ if (xkey) {
+ if (term->vt52_mode) {
+ if (xkey >= 'P' && xkey <= 'S')
+ p += sprintf((char *) p, "\x1B%c", xkey);
+ else
+ p += sprintf((char *) p, "\x1B?%c", xkey);
+ } else
+ p += sprintf((char *) p, "\x1BO%c", xkey);
+ goto done;
+ }
+ /* Not in application mode -- treat the number pad as arrow keys? */
+ if ((flags & PKF_NUMLOCK) == 0) {
+ switch (keysym) {
+ case PK_KP0: keysym = PK_INSERT; break;
+ case PK_KP1: keysym = PK_END; break;
+ case PK_KP2: keysym = PK_DOWN; break;
+ case PK_KP3: keysym = PK_PAGEDOWN; break;
+ case PK_KP4: keysym = PK_LEFT; break;
+ case PK_KP5: keysym = PK_REST; break;
+ case PK_KP6: keysym = PK_RIGHT; break;
+ 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' */
+ }
+ }
+ }
+
+ /* Miscellaneous keys */
+ switch (keysym) {
+ case PK_ESCAPE:
+ *p++ = 0x1b;
+ goto done;
+ case PK_BACKSPACE:
+ if (modifiers == 0)
+ *p++ = (term->cfg.bksp_is_delete ? 0x7F : 0x08);
+ else if (modifiers == PKM_SHIFT)
+ /* We do the opposite of what is configured */
+ *p++ = (term->cfg.bksp_is_delete ? 0x08 : 0x7F);
+ else break;
+ goto done;
+ case PK_TAB:
+ if (modifiers == 0)
+ *p++ = 0x09;
+ else if (modifiers == PKM_SHIFT)
+ *p++ = 0x1B, *p++ = '[', *p++ = 'Z';
+ else break;
+ goto done;
+ /* XXX window.c has ctrl+shift+space sending 0xa0 */
+ case PK_PAUSE:
+ if (modifiers == PKM_CONTROL)
+ *p++ = 26;
+ else break;
+ goto done;
+ case PK_RETURN:
+ case PK_KPENTER: /* Odd keypad modes handled above */
+ if (modifiers == 0) {
+ *p++ = 0x0d;
+ if (term->cr_lf_return)
+ *p++ = 0x0a;
+ goto done;
+ }
+ default: break; /* else gcc warns `enum value not used' */
+ }
+
+ /* SCO function keys and editing keys */
+ if (term->cfg.funky_type == FUNKY_SCO) {
+ if (PK_ISFKEY(keysym) && keysym <= PK_F12) {
+ static char const codes[] =
+ "MNOPQRSTUVWX" "YZabcdefghij" "klmnopqrstuv" "wxyz@[\\]^_`{";
+ int index = keysym - PK_F1;
+
+ if (modifiers & PKM_SHIFT) index += 12;
+ if (modifiers & PKM_CONTROL) index += 24;
+ p += sprintf((char *) p, "\x1B[%c", codes[index]);
+ goto done;
+ }
+ if (PK_ISEDITING(keysym)) {
+ int xkey = 0;
+
+ switch (keysym) {
+ case PK_DELETE: *p++ = 0x7f; goto done;
+ case PK_HOME: xkey = 'H'; break;
+ case PK_INSERT: xkey = 'L'; break;
+ 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);
+ }
+ }
+
+ if (PK_ISEDITING(keysym) && (modifiers & PKM_SHIFT) == 0) {
+ int code;
+
+ if (term->cfg.funky_type == FUNKY_XTERM) {
+ /* Xterm shuffles these keys, apparently. */
+ switch (keysym) {
+ case PK_HOME: keysym = PK_INSERT; break;
+ case PK_INSERT: keysym = PK_HOME; break;
+ case PK_DELETE: keysym = PK_END; break;
+ 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' */
+ }
+ }
+
+ /* RXVT Home/End */
+ if (term->cfg.rxvt_homeend &&
+ (keysym == PK_HOME || keysym == PK_END)) {
+ p += sprintf((char *) p, keysym == PK_HOME ? "\x1B[H" : "\x1BOw");
+ goto done;
+ }
+
+ if (term->vt52_mode) {
+ int xkey;
+
+ /*
+ * A real VT52 doesn't have these, and a VT220 doesn't
+ * send anything for them in VT52 mode.
+ */
+ switch (keysym) {
+ case PK_HOME: xkey = 'H'; break;
+ case PK_INSERT: xkey = 'L'; break;
+ case PK_DELETE: xkey = 'M'; break;
+ case PK_END: xkey = 'E'; break;
+ case PK_PAGEUP: xkey = 'I'; break;
+ case PK_PAGEDOWN: xkey = 'G'; break;
+ default: xkey=0; break; /* else gcc warns `enum value not used'*/
+ }
+ p += sprintf((char *) p, "\x1B%c", xkey);
+ goto done;
+ }
+
+ switch (keysym) {
+ case PK_HOME: code = 1; break;
+ case PK_INSERT: code = 2; break;
+ case PK_DELETE: code = 3; break;
+ case PK_END: code = 4; break;
+ case PK_PAGEUP: code = 5; break;
+ case PK_PAGEDOWN: code = 6; break;
+ default: code = 0; break; /* else gcc warns `enum value not used' */
+ }
+ p += sprintf((char *) p, "\x1B[%d~", code);
+ goto done;
+ }
+
+ if (PK_ISFKEY(keysym)) {
+ /* Map Shift+F1-F10 to F11-F20 */
+ if (keysym >= PK_F1 && keysym <= PK_F10 && (modifiers & PKM_SHIFT))
+ keysym += 10;
+ if ((term->vt52_mode || term->cfg.funky_type == FUNKY_VT100P) &&
+ keysym <= PK_F14) {
+ /* XXX This overrides the XTERM/VT52 mode below */
+ int offt = 0;
+ if (keysym >= PK_F6) offt++;
+ if (keysym >= PK_F12) offt++;
+ p += sprintf((char *) p, term->vt52_mode ? "\x1B%c" : "\x1BO%c",
+ 'P' + keysym - PK_F1 - offt);
+ goto done;
+ }
+ if (term->cfg.funky_type == FUNKY_LINUX && keysym <= PK_F5) {
+ p += sprintf((char *) p, "\x1B[[%c", 'A' + keysym - PK_F1);
+ goto done;
+ }
+ if (term->cfg.funky_type == FUNKY_XTERM && keysym <= PK_F4) {
+ if (term->vt52_mode)
+ p += sprintf((char *) p, "\x1B%c", 'P' + keysym - PK_F1);
+ else
+ p += sprintf((char *) p, "\x1BO%c", 'P' + keysym - PK_F1);
+ goto done;
+ }
+ p += sprintf((char *) p, "\x1B[%d~", 11 + keysym - PK_F1);
+ goto done;
+ }
+
+ if (PK_ISCURSOR(keysym)) {
+ int xkey;
+
+ switch (keysym) {
+ case PK_UP: xkey = 'A'; break;
+ case PK_DOWN: xkey = 'B'; break;
+ case PK_RIGHT: xkey = 'C'; break;
+ case PK_LEFT: xkey = 'D'; break;
+ case PK_REST: xkey = 'G'; break; /* centre key on number pad */
+ default: xkey = 0; break; /* else gcc warns `enum value not used' */
+ }
+ if (term->vt52_mode)
+ p += sprintf((char *) p, "\x1B%c", xkey);
+ else {
+ int app_flg = (term->app_cursor_keys && !term->cfg.no_applic_c);
+
+ /* Useful mapping of Ctrl-arrows */
+ if (modifiers == PKM_CONTROL)
+ app_flg = !app_flg;
+
+ if (app_flg)
+ p += sprintf((char *) p, "\x1BO%c", xkey);
+ else
+ p += sprintf((char *) p, "\x1B[%c", xkey);
+ }
+ goto done;
+ }
+
+ done:
+ if (p > output || tlen > 0) {
+ /*
+ * Interrupt an ongoing paste. I'm not sure
+ * this is sensible, but for the moment it's
+ * preferable to having to faff about buffering
+ * things.
+ */
+ term_nopaste(term);
+
+ /*
+ * We need not bother about stdin backlogs
+ * here, because in GUI PuTTY we can't do
+ * anything about it anyway; there's no means
+ * of asking Windows to hold off on KEYDOWN
+ * messages. We _have_ to buffer everything
+ * we're sent.
+ */
+ 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", (unsigned) text[i]);
+ fprintf(stderr, "\n");
+#endif
+ luni_send(term->ldisc, text, tlen, 1);
+ }
+ }
+}
+
void term_nopaste(Terminal *term)
{
if (term->paste_len == 0)
return FALSE;
}
-/*
- * from_backend(), to get data from the backend for the terminal.
- */
-int from_backend(void *vterm, int is_stderr, char *data, int len)
+int term_data(Terminal *term, int is_stderr, const char *data, int len)
{
- Terminal *term = (Terminal *)vterm;
-
- assert(len > 0);
-
bufchain_add(&term->inbuf, data, len);
+ if (!term->in_term_out) {
+ term->in_term_out = TRUE;
+ term_blink(term, 1);
+ term_out(term);
+ term->in_term_out = FALSE;
+ }
+
/*
* term_out() always completely empties inbuf. Therefore,
* there's no reason at all to return anything other than zero