mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-03-22 14:39:24 -05:00
Cursor position, selection highlights and mouse clicks are now all
transformed back and forth according to the character position permutation output from the bidi algorithm. I was expecting that to be a lot harder. [originally from svn r4915]
This commit is contained in:
parent
c66ac86b91
commit
569da2eb7b
295
terminal.c
295
terminal.c
@ -4317,8 +4317,11 @@ static int term_bidi_cache_hit(Terminal *term, int line,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void term_bidi_cache_store(Terminal *term, int line, termchar *lbefore,
|
static void term_bidi_cache_store(Terminal *term, int line, termchar *lbefore,
|
||||||
termchar *lafter, int width)
|
termchar *lafter, bidi_char *wcTo,
|
||||||
|
int width)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
if (!term->pre_bidi_cache || term->bidi_cache_size <= line) {
|
if (!term->pre_bidi_cache || term->bidi_cache_size <= line) {
|
||||||
int j = term->bidi_cache_size;
|
int j = term->bidi_cache_size;
|
||||||
term->bidi_cache_size = line+1;
|
term->bidi_cache_size = line+1;
|
||||||
@ -4344,127 +4347,42 @@ static void term_bidi_cache_store(Terminal *term, int line, termchar *lbefore,
|
|||||||
term->pre_bidi_cache[line].chars = snewn(width, termchar);
|
term->pre_bidi_cache[line].chars = snewn(width, termchar);
|
||||||
term->post_bidi_cache[line].width = width;
|
term->post_bidi_cache[line].width = width;
|
||||||
term->post_bidi_cache[line].chars = snewn(width, termchar);
|
term->post_bidi_cache[line].chars = snewn(width, termchar);
|
||||||
|
term->post_bidi_cache[line].forward = snewn(width, int);
|
||||||
|
term->post_bidi_cache[line].backward = snewn(width, int);
|
||||||
|
|
||||||
memcpy(term->pre_bidi_cache[line].chars, lbefore, width * TSIZE);
|
memcpy(term->pre_bidi_cache[line].chars, lbefore, width * TSIZE);
|
||||||
memcpy(term->post_bidi_cache[line].chars, lafter, width * TSIZE);
|
memcpy(term->post_bidi_cache[line].chars, lafter, width * TSIZE);
|
||||||
|
memset(term->post_bidi_cache[line].forward, 0, width * sizeof(int));
|
||||||
|
memset(term->post_bidi_cache[line].backward, 0, width * sizeof(int));
|
||||||
|
|
||||||
|
for (i = 0; i < width; i++) {
|
||||||
|
int p = wcTo[i].index;
|
||||||
|
|
||||||
|
assert(0 <= p && p < width);
|
||||||
|
|
||||||
|
term->post_bidi_cache[line].backward[i] = p;
|
||||||
|
term->post_bidi_cache[line].forward[p] = i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Given a context, update the window. Out of paranoia, we don't
|
* Prepare the bidi information for a screen line. Returns the
|
||||||
* allow WM_PAINT responses to do scrolling optimisations.
|
* transformed list of termchars, or NULL if no transformation at
|
||||||
|
* all took place (because bidi is disabled). If return was
|
||||||
|
* non-NULL, auxiliary information such as the forward and reverse
|
||||||
|
* mappings of permutation position are available in
|
||||||
|
* term->post_bidi_cache[scr_y].*.
|
||||||
*/
|
*/
|
||||||
static void do_paint(Terminal *term, Context ctx, int may_optimise)
|
static termchar *term_bidi_line(Terminal *term, struct termline *ldata,
|
||||||
|
int scr_y)
|
||||||
{
|
{
|
||||||
int i, it, j, our_curs_y, our_curs_x;
|
|
||||||
int rv, cursor;
|
|
||||||
pos scrpos;
|
|
||||||
wchar_t *ch;
|
|
||||||
int chlen;
|
|
||||||
termchar cursor_background;
|
|
||||||
#ifdef OPTIMISE_SCROLL
|
|
||||||
struct scrollregion *sr;
|
|
||||||
#endif /* OPTIMISE_SCROLL */
|
|
||||||
|
|
||||||
cursor_background = term->basic_erase_char;
|
|
||||||
|
|
||||||
chlen = 1024;
|
|
||||||
ch = snewn(chlen, wchar_t);
|
|
||||||
|
|
||||||
rv = (!term->rvideo ^ !term->in_vbell ? ATTR_REVERSE : 0);
|
|
||||||
|
|
||||||
/* Depends on:
|
|
||||||
* screen array, disptop, scrtop,
|
|
||||||
* selection, rv,
|
|
||||||
* cfg.blinkpc, blink_is_real, tblinker,
|
|
||||||
* curs.y, curs.x, cblinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Has the cursor position or type changed ? */
|
|
||||||
if (term->cursor_on) {
|
|
||||||
if (term->has_focus) {
|
|
||||||
if (term->cblinker || !term->cfg.blink_cur)
|
|
||||||
cursor = TATTR_ACTCURS;
|
|
||||||
else
|
|
||||||
cursor = 0;
|
|
||||||
} else
|
|
||||||
cursor = TATTR_PASCURS;
|
|
||||||
if (term->wrapnext)
|
|
||||||
cursor |= TATTR_RIGHTCURS;
|
|
||||||
} 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.
|
|
||||||
*/
|
|
||||||
termline *ldata = lineptr(term->curs.y);
|
|
||||||
our_curs_x = term->curs.x;
|
|
||||||
if (our_curs_x > 0 &&
|
|
||||||
ldata->chars[our_curs_x].chr == UCSWIDE)
|
|
||||||
our_curs_x--;
|
|
||||||
unlineptr(ldata);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the cursor is not where it was last time we painted, and
|
|
||||||
* its previous position is visible on screen, invalidate its
|
|
||||||
* previous position.
|
|
||||||
*/
|
|
||||||
if (term->dispcursy >= 0 &&
|
|
||||||
(term->curstype != cursor ||
|
|
||||||
term->dispcursy != our_curs_y ||
|
|
||||||
term->dispcursx != our_curs_x)) {
|
|
||||||
termchar *dispcurs = term->disptext[term->dispcursy]->chars +
|
|
||||||
term->dispcursx;
|
|
||||||
|
|
||||||
if (term->dispcursx > 0 && dispcurs->chr == UCSWIDE)
|
|
||||||
dispcurs[-1].attr |= ATTR_INVALID;
|
|
||||||
if (term->dispcursx < term->cols-1 && dispcurs[1].chr == UCSWIDE)
|
|
||||||
dispcurs[1].attr |= ATTR_INVALID;
|
|
||||||
dispcurs->attr |= ATTR_INVALID;
|
|
||||||
|
|
||||||
term->curstype = 0;
|
|
||||||
}
|
|
||||||
term->dispcursx = term->dispcursy = -1;
|
|
||||||
|
|
||||||
#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++) {
|
|
||||||
termline *ldata;
|
|
||||||
termchar *lchars;
|
termchar *lchars;
|
||||||
int dirty_line, dirty_run, selected;
|
int it;
|
||||||
unsigned long attr = 0, cset = 0;
|
|
||||||
int updated_line = 0;
|
|
||||||
int start = 0;
|
|
||||||
int ccount = 0;
|
|
||||||
int last_run_dirty = 0;
|
|
||||||
|
|
||||||
scrpos.y = i + term->disptop;
|
|
||||||
ldata = lineptr(scrpos.y);
|
|
||||||
|
|
||||||
dirty_run = dirty_line = (ldata->lattr !=
|
|
||||||
term->disptext[i]->lattr);
|
|
||||||
term->disptext[i]->lattr = ldata->lattr;
|
|
||||||
|
|
||||||
/* Do Arabic shaping and bidi. */
|
/* Do Arabic shaping and bidi. */
|
||||||
if(!term->cfg.bidi || !term->cfg.arabicshaping) {
|
if(!term->cfg.bidi || !term->cfg.arabicshaping) {
|
||||||
|
|
||||||
if (!term_bidi_cache_hit(term, i, ldata->chars, term->cols)) {
|
if (!term_bidi_cache_hit(term, scr_y, ldata->chars, term->cols)) {
|
||||||
|
|
||||||
if (term->wcFromTo_size < term->cols) {
|
if (term->wcFromTo_size < term->cols) {
|
||||||
term->wcFromTo_size = term->cols;
|
term->wcFromTo_size = term->cols;
|
||||||
@ -4534,21 +4452,158 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
|
|||||||
if (term->wcTo[it].origwc != term->wcTo[it].wc)
|
if (term->wcTo[it].origwc != term->wcTo[it].wc)
|
||||||
term->ltemp[it].chr = term->wcTo[it].wc;
|
term->ltemp[it].chr = term->wcTo[it].wc;
|
||||||
}
|
}
|
||||||
term_bidi_cache_store(term, i, ldata->chars,
|
term_bidi_cache_store(term, scr_y, ldata->chars,
|
||||||
term->ltemp, ldata->size);
|
term->ltemp, term->wcTo, ldata->size);
|
||||||
|
|
||||||
lchars = term->ltemp;
|
lchars = term->ltemp;
|
||||||
} else {
|
} else {
|
||||||
lchars = term->post_bidi_cache[i].chars;
|
lchars = term->post_bidi_cache[scr_y].chars;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
lchars = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lchars;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a context, update the window. Out of paranoia, we don't
|
||||||
|
* allow WM_PAINT responses to do scrolling optimisations.
|
||||||
|
*/
|
||||||
|
static void do_paint(Terminal *term, Context ctx, int may_optimise)
|
||||||
|
{
|
||||||
|
int i, j, our_curs_y, our_curs_x;
|
||||||
|
int rv, cursor;
|
||||||
|
pos scrpos;
|
||||||
|
wchar_t *ch;
|
||||||
|
int chlen;
|
||||||
|
termchar cursor_background;
|
||||||
|
#ifdef OPTIMISE_SCROLL
|
||||||
|
struct scrollregion *sr;
|
||||||
|
#endif /* OPTIMISE_SCROLL */
|
||||||
|
|
||||||
|
cursor_background = term->basic_erase_char;
|
||||||
|
|
||||||
|
chlen = 1024;
|
||||||
|
ch = snewn(chlen, wchar_t);
|
||||||
|
|
||||||
|
rv = (!term->rvideo ^ !term->in_vbell ? ATTR_REVERSE : 0);
|
||||||
|
|
||||||
|
/* Depends on:
|
||||||
|
* screen array, disptop, scrtop,
|
||||||
|
* selection, rv,
|
||||||
|
* cfg.blinkpc, blink_is_real, tblinker,
|
||||||
|
* curs.y, curs.x, cblinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Has the cursor position or type changed ? */
|
||||||
|
if (term->cursor_on) {
|
||||||
|
if (term->has_focus) {
|
||||||
|
if (term->cblinker || !term->cfg.blink_cur)
|
||||||
|
cursor = TATTR_ACTCURS;
|
||||||
|
else
|
||||||
|
cursor = 0;
|
||||||
|
} else
|
||||||
|
cursor = TATTR_PASCURS;
|
||||||
|
if (term->wrapnext)
|
||||||
|
cursor |= TATTR_RIGHTCURS;
|
||||||
|
} else
|
||||||
|
cursor = 0;
|
||||||
|
our_curs_y = term->curs.y - term->disptop;
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Adjust the cursor position:
|
||||||
|
* - for bidi
|
||||||
|
* - 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.
|
||||||
|
*/
|
||||||
|
termline *ldata = lineptr(term->curs.y);
|
||||||
|
termchar *lchars;
|
||||||
|
|
||||||
|
our_curs_x = term->curs.x;
|
||||||
|
|
||||||
|
if ( (lchars = term_bidi_line(term, ldata, our_curs_y)) != NULL) {
|
||||||
|
our_curs_x = term->post_bidi_cache[our_curs_y].forward[our_curs_x];
|
||||||
} else
|
} else
|
||||||
lchars = ldata->chars;
|
lchars = ldata->chars;
|
||||||
|
|
||||||
|
if (our_curs_x > 0 &&
|
||||||
|
lchars[our_curs_x].chr == UCSWIDE)
|
||||||
|
our_curs_x--;
|
||||||
|
|
||||||
|
unlineptr(ldata);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the cursor is not where it was last time we painted, and
|
||||||
|
* its previous position is visible on screen, invalidate its
|
||||||
|
* previous position.
|
||||||
|
*/
|
||||||
|
if (term->dispcursy >= 0 &&
|
||||||
|
(term->curstype != cursor ||
|
||||||
|
term->dispcursy != our_curs_y ||
|
||||||
|
term->dispcursx != our_curs_x)) {
|
||||||
|
termchar *dispcurs = term->disptext[term->dispcursy]->chars +
|
||||||
|
term->dispcursx;
|
||||||
|
|
||||||
|
if (term->dispcursx > 0 && dispcurs->chr == UCSWIDE)
|
||||||
|
dispcurs[-1].attr |= ATTR_INVALID;
|
||||||
|
if (term->dispcursx < term->cols-1 && dispcurs[1].chr == UCSWIDE)
|
||||||
|
dispcurs[1].attr |= ATTR_INVALID;
|
||||||
|
dispcurs->attr |= ATTR_INVALID;
|
||||||
|
|
||||||
|
term->curstype = 0;
|
||||||
|
}
|
||||||
|
term->dispcursx = term->dispcursy = -1;
|
||||||
|
|
||||||
|
#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++) {
|
||||||
|
termline *ldata;
|
||||||
|
termchar *lchars;
|
||||||
|
int dirty_line, dirty_run, selected;
|
||||||
|
unsigned long attr = 0, cset = 0;
|
||||||
|
int updated_line = 0;
|
||||||
|
int start = 0;
|
||||||
|
int ccount = 0;
|
||||||
|
int last_run_dirty = 0;
|
||||||
|
int *backward;
|
||||||
|
|
||||||
|
scrpos.y = i + term->disptop;
|
||||||
|
ldata = lineptr(scrpos.y);
|
||||||
|
|
||||||
|
dirty_run = dirty_line = (ldata->lattr !=
|
||||||
|
term->disptext[i]->lattr);
|
||||||
|
term->disptext[i]->lattr = ldata->lattr;
|
||||||
|
|
||||||
|
/* Do Arabic shaping and bidi. */
|
||||||
|
lchars = term_bidi_line(term, ldata, i);
|
||||||
|
if (lchars) {
|
||||||
|
backward = term->post_bidi_cache[i].backward;
|
||||||
|
} else {
|
||||||
|
lchars = ldata->chars;
|
||||||
|
backward = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
for (j = 0; j < term->cols; j++) {
|
for (j = 0; j < term->cols; j++) {
|
||||||
unsigned long tattr, tchar;
|
unsigned long tattr, tchar;
|
||||||
termchar *d = lchars + j;
|
termchar *d = lchars + j;
|
||||||
int break_run, do_copy;
|
int break_run, do_copy;
|
||||||
scrpos.x = j;
|
scrpos.x = backward ? backward[j] : j;
|
||||||
|
|
||||||
tchar = d->chr;
|
tchar = d->chr;
|
||||||
tattr = d->attr;
|
tattr = d->attr;
|
||||||
@ -5341,10 +5396,20 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked,
|
|||||||
x = term->cols - 1;
|
x = term->cols - 1;
|
||||||
|
|
||||||
selpoint.y = y + term->disptop;
|
selpoint.y = y + term->disptop;
|
||||||
selpoint.x = x;
|
|
||||||
ldata = lineptr(selpoint.y);
|
ldata = lineptr(selpoint.y);
|
||||||
|
|
||||||
if ((ldata->lattr & LATTR_MODE) != LATTR_NORM)
|
if ((ldata->lattr & LATTR_MODE) != LATTR_NORM)
|
||||||
selpoint.x /= 2;
|
x /= 2;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transform x through the bidi algorithm to find the _logical_
|
||||||
|
* click point from the physical one.
|
||||||
|
*/
|
||||||
|
if (term_bidi_line(term, ldata, y) != NULL) {
|
||||||
|
x = term->post_bidi_cache[y].backward[x];
|
||||||
|
}
|
||||||
|
|
||||||
|
selpoint.x = x;
|
||||||
unlineptr(ldata);
|
unlineptr(ldata);
|
||||||
|
|
||||||
if (raw_mouse) {
|
if (raw_mouse) {
|
||||||
|
@ -68,6 +68,7 @@ struct termline {
|
|||||||
struct bidi_cache_entry {
|
struct bidi_cache_entry {
|
||||||
int width;
|
int width;
|
||||||
struct termchar *chars;
|
struct termchar *chars;
|
||||||
|
int *forward, *backward; /* the permutations of line positions */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct terminal_tag {
|
struct terminal_tag {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user