X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/c6958dfe2ab270f7d02d02b21d4a4008478a5ea9..73feed4f401660e3374ea0f783ae7b7c6ea4c0c8:/terminal.c diff --git a/terminal.c b/terminal.c index a9785dce..40ad4823 100644 --- a/terminal.c +++ b/terminal.c @@ -131,6 +131,11 @@ static void unlineptr(termline *line) /* * Diagnostic function: verify that a termline has a correct * combining character structure. + * + * XXX-REMOVE-BEFORE-RELEASE: This is a performance-intensive + * check. Although it's currently really useful for getting all the + * bugs out of the new cc stuff, it will want to be absent when we + * make a proper release. */ static void cc_check(termline *line) { @@ -172,6 +177,8 @@ static void cc_check(termline *line) j += (flags[i] != 0); assert(j == line->size); + + sfree(flags); } /* @@ -219,7 +226,7 @@ static void add_cc(termline *line, int col, unsigned long chr) line->chars[newcc].chr = chr; line->chars[col].cc_next = newcc - col; - cc_check(line); + cc_check(line); /* XXX-REMOVE-BEFORE-RELEASE */ } /* @@ -245,7 +252,7 @@ static void clear_cc(termline *line, int col) line->chars[origcol].cc_next = 0; - cc_check(line); + cc_check(line); /* XXX-REMOVE-BEFORE-RELEASE */ } /* @@ -292,6 +299,8 @@ static void copy_termchar(termline *destline, int x, termchar *src) src += src->cc_next; add_cc(destline, x, src->chr); } + + cc_check(destline); /* XXX-REMOVE-BEFORE-RELEASE */ } /* @@ -309,6 +318,8 @@ static void move_termchar(termline *line, termchar *dest, termchar *src) /* Ensure the original cell doesn't have a cc list. */ src->cc_next = 0; + + cc_check(line); /* XXX-REMOVE-BEFORE-RELEASE */ } /* @@ -625,6 +636,9 @@ static unsigned char *compressline(termline *ldata) /* * Diagnostics: ensure that the compressed data really does * decompress to the right thing. + * + * XXX-REMOVE-BEFORE-RELEASE: This is a bit performance-heavy + * to be leaving in production code. */ #ifndef CHECK_SB_COMPRESSION { @@ -836,9 +850,12 @@ static termline *decompressline(unsigned char *data, int *bytes_used) */ static void resizeline(Terminal *term, termline *line, int cols) { - int i, oldlen; + int i, oldcols; if (line->cols != cols) { + + oldcols = line->cols; + /* * This line is the wrong length, which probably means it * hasn't been accessed since a resize. Resize it now. @@ -847,24 +864,36 @@ static void resizeline(Terminal *term, termline *line, int cols) * out in the resize (if we're shrinking the line) and * return their cc lists to the cc free list. */ - for (i = cols; i < line->cols; i++) + for (i = cols; i < oldcols; i++) clear_cc(line, i); /* + * If we're shrinking the line, we now bodily move the + * entire cc section from where it started to where it now + * needs to be. (We have to do this before the resize, so + * that the data we're copying is still there. However, if + * we're expanding, we have to wait until _after_ the + * resize so that the space we're copying into is there.) + */ + if (cols < oldcols) + memmove(line->chars + cols, line->chars + oldcols, + (line->size - line->cols) * TSIZE); + + /* * Now do the actual resize, leaving the _same_ amount of * cc space as there was to begin with. */ - oldlen = line->cols; - line->size += cols - oldlen; + line->size += cols - oldcols; line->chars = sresize(line->chars, line->size, TTYPE); line->cols = cols; /* - * Bodily move the entire cc section from where it started - * to where it now needs to be. + * If we're expanding the line, _now_ we move the cc + * section. */ - memmove(line->chars + line->cols, line->chars + oldlen, - (line->size - line->cols) * TSIZE); + if (cols > oldcols) + memmove(line->chars + cols, line->chars + oldcols, + (line->size - line->cols) * TSIZE); /* * Go through what's left of the original line, and adjust @@ -873,21 +902,21 @@ static void resizeline(Terminal *term, termline *line, int cols) * relative offsets within the cc block.) Also do the same * to the head of the cc_free list. */ - for (i = 0; i < oldlen && i < line->cols; i++) + for (i = 0; i < oldcols && i < cols; i++) if (line->chars[i].cc_next) - line->chars[i].cc_next += cols - oldlen; + line->chars[i].cc_next += cols - oldcols; if (line->cc_free) - line->cc_free += cols - oldlen; + line->cc_free += cols - oldcols; /* * And finally fill in the new space with erase chars. (We * don't have to worry about cc lists here, because we * _know_ the erase char doesn't have one.) */ - for (i = oldlen; i < cols; i++) + for (i = oldcols; i < cols; i++) line->chars[i] = term->basic_erase_char; - cc_check(line); + cc_check(line); /* XXX-REMOVE-BEFORE-RELEASE */ } } @@ -1110,6 +1139,22 @@ void term_reconfig(Terminal *term, Config *cfg) if (term->cfg.wordness[i] != cfg->wordness[i]) reset_charclass = 1; + /* + * If the bidi or shaping settings have changed, flush the bidi + * cache completely. + */ + if (term->cfg.arabicshaping != cfg->arabicshaping || + term->cfg.bidi != cfg->bidi) { + for (i = 0; i < term->bidi_cache_size; i++) { + sfree(term->pre_bidi_cache[i].chars); + sfree(term->post_bidi_cache[i].chars); + term->pre_bidi_cache[i].width = -1; + term->pre_bidi_cache[i].chars = NULL; + term->post_bidi_cache[i].width = -1; + term->post_bidi_cache[i].chars = NULL; + } + } + term->cfg = *cfg; /* STRUCTURE COPY */ if (reset_wrap) @@ -1270,8 +1315,8 @@ void term_free(Terminal *term) sfree(term->wcTo); for (i = 0; i < term->bidi_cache_size; i++) { - sfree(term->pre_bidi_cache[i]); - sfree(term->post_bidi_cache[i]); + sfree(term->pre_bidi_cache[i].chars); + sfree(term->post_bidi_cache[i].chars); } sfree(term->pre_bidi_cache); sfree(term->post_bidi_cache); @@ -1605,7 +1650,7 @@ static void scroll(Terminal *term, int topline, int botline, int lines, int sb) } else { while (lines > 0) { line = delpos234(term->screen, topline); - cc_check(line); + cc_check(line); /* XXX-REMOVE-BEFORE-RELEASE */ if (sb && term->savelines > 0) { int sblen = count234(term->scrollback); /* @@ -1624,7 +1669,7 @@ static void scroll(Terminal *term, int topline, int botline, int lines, int sb) addpos234(term->scrollback, compressline(line), sblen); - line = newline(term, term->cols, TRUE); + /* now `line' itself can be reused as the bottom line */ /* * If the user is currently looking at part of the @@ -2421,6 +2466,8 @@ void term_out(Terminal *term) term->wrapnext = FALSE; /* destructive backspace might be disabled */ if (!term->cfg.no_dbackspace) { + check_boundary(term, term->curs.x, term->curs.y); + check_boundary(term, term->curs.x+1, term->curs.y); copy_termchar(scrlineptr(term->curs.y), term->curs.x, &term->erase_char); } @@ -2631,6 +2678,7 @@ void term_out(Terminal *term) term->curs.y++; term->curs.x = 0; term->wrapnext = FALSE; + cline = scrlineptr(term->curs.y); } if (term->insert && width > 0) insch(term, width); @@ -2707,7 +2755,26 @@ void term_out(Terminal *term) break; case 0: - add_cc(cline, term->curs.x - !term->wrapnext, c); + if (term->curs.x > 0) { + int x = term->curs.x - 1; + + /* If we're in wrapnext state, the character + * to combine with is _here_, not to our left. */ + if (term->wrapnext) + x++; + + /* + * If the previous character is + * UCSWIDE, back up another one. + */ + if (cline->chars[x].chr == UCSWIDE) { + assert(x > 0); + x--; + } + + add_cc(cline, x, c); + term->seen_disp_event = 1; + } continue; default: continue; @@ -4117,11 +4184,14 @@ static int term_bidi_cache_hit(Terminal *term, int line, if (line >= term->bidi_cache_size) return FALSE; /* cache doesn't have this many lines */ - if (!term->pre_bidi_cache[line]) + if (!term->pre_bidi_cache[line].chars) return FALSE; /* cache doesn't contain _this_ line */ + if (term->pre_bidi_cache[line].width != width) + return FALSE; /* line is wrong width */ + for (i = 0; i < width; i++) - if (!termchars_equal(term->pre_bidi_cache[line] + i, lbefore + i)) + if (!termchars_equal(term->pre_bidi_cache[line].chars+i, lbefore+i)) return FALSE; /* line doesn't match cache */ return TRUE; /* it didn't match. */ @@ -4135,24 +4205,29 @@ static void term_bidi_cache_store(Terminal *term, int line, termchar *lbefore, term->bidi_cache_size = line+1; term->pre_bidi_cache = sresize(term->pre_bidi_cache, term->bidi_cache_size, - termchar *); + struct bidi_cache_entry); term->post_bidi_cache = sresize(term->post_bidi_cache, term->bidi_cache_size, - termchar *); + struct bidi_cache_entry); while (j < term->bidi_cache_size) { - term->pre_bidi_cache[j] = term->post_bidi_cache[j] = NULL; + term->pre_bidi_cache[j].chars = + term->post_bidi_cache[j].chars = NULL; + term->pre_bidi_cache[j].width = + term->post_bidi_cache[j].width = -1; j++; } } - sfree(term->pre_bidi_cache[line]); - sfree(term->post_bidi_cache[line]); + sfree(term->pre_bidi_cache[line].chars); + sfree(term->post_bidi_cache[line].chars); - term->pre_bidi_cache[line] = snewn(width, termchar); - term->post_bidi_cache[line] = snewn(width, termchar); + term->pre_bidi_cache[line].width = width; + term->pre_bidi_cache[line].chars = snewn(width, termchar); + term->post_bidi_cache[line].width = width; + term->post_bidi_cache[line].chars = snewn(width, termchar); - memcpy(term->pre_bidi_cache[line], lbefore, width * TSIZE); - memcpy(term->post_bidi_cache[line], lafter, width * TSIZE); + memcpy(term->pre_bidi_cache[line].chars, lbefore, width * TSIZE); + memcpy(term->post_bidi_cache[line].chars, lafter, width * TSIZE); } /* @@ -4355,7 +4430,7 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise) lchars = term->ltemp; } else { - lchars = term->post_bidi_cache[i]; + lchars = term->post_bidi_cache[i].chars; } } else lchars = ldata->chars; @@ -4550,6 +4625,7 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise) if (i == our_curs_y && (term->curstype != cursor || updated_line)) { ch[0] = (wchar_t) cursor_background.chr; attr = cursor_background.attr | cursor; + ccount = 1; if (cursor_background.cc_next) { termchar *dd = ldata->chars + cursor_background.cc_next; @@ -4585,7 +4661,7 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise) attr |= TATTR_COMBINING; } - do_cursor(ctx, our_curs_x, i, ch, 1, attr, ldata->lattr); + do_cursor(ctx, our_curs_x, i, ch, ccount, attr, ldata->lattr); term->curstype = cursor; } @@ -4653,7 +4729,7 @@ void term_paint(Terminal *term, Context ctx, if (bottom >= term->rows) bottom = term->rows-1; for (i = top; i <= bottom && i < term->rows; i++) { - if (term->disptext[i]->lattr == LATTR_NORM) + if ((term->disptext[i]->lattr & LATTR_MODE) == LATTR_NORM) for (j = left; j <= right && j < term->cols; j++) term->disptext[i]->chars[j].attr = ATTR_INVALID; else @@ -4732,6 +4808,7 @@ static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel) */ if (!(ldata->lattr & LATTR_WRAPPED)) { while (IS_SPACE_CHR(ldata->chars[nlpos.x - 1].chr) && + !ldata->chars[nlpos.x - 1].cc_next && poslt(top, nlpos)) decpos(nlpos); if (poslt(nlpos, bottom)) @@ -5004,7 +5081,8 @@ static pos sel_spread_half(Terminal *term, pos p, int dir) */ if (!(ldata->lattr & LATTR_WRAPPED)) { termchar *q = ldata->chars + term->cols; - while (q > ldata->chars && IS_SPACE_CHR(q[-1].chr)) + while (q > ldata->chars && + IS_SPACE_CHR(q[-1].chr) && !q[-1].cc_next) q--; if (q == ldata->chars + term->cols) q--;