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);
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);
}
/*
+ * 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.
*/
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. */
ldata = lineptr(start.y);
while (poslt(start, end)) {
if (start.x == term->cols && !erase_lattr)
- ldata[start.x] &= ~LATTR_WRAPPED;
+ ldata[start.x] &= ~(LATTR_WRAPPED | LATTR_WRAPPED2);
else
ldata[start.x] = term->erase_char;
if (incpos(start) && start.y < term->rows)
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);
else if(term->sco_acs &&
(c!='\033' && c!='\012' && c!='\015' && c!='\b'))
{
- if (term->sco_acs == 2) c ^= 0x80;
+ if (term->sco_acs == 2) c |= 0x80;
c |= ATTR_SCOACS;
} else {
switch (term->cset_attr[term->cset]) {
term->logctx)
logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII);
{
- extern int wcwidth(wchar_t ucs);
int width = 0;
if (DIRECT_CHAR(c))
width = 1;
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:
compatibility(SCOANSI);
if (term->cfg->no_remote_charset) break;
term->sco_acs = 1; break;
- case 12: /* SCO acs on flipped */
+ case 12: /* SCO acs on, |0x80 */
compatibility(SCOANSI);
if (term->cfg->no_remote_charset) break;
term->sco_acs = 2; break;
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;
*/
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];
} 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;
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 (++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;
}
}
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);
}
/*
* 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;
}