* store a two-byte value with the top bit clear (indicating
* just that value), or a four-byte value with the top bit set
* (indicating the same value with its top bit clear).
+ *
+ * However, first I permute the bits of the attribute value, so
+ * that the eight bits of colour (four in each of fg and bg)
+ * which are never non-zero unless xterm 256-colour mode is in
+ * use are placed higher up the word than everything else. This
+ * ensures that attribute values remain 16-bit _unless_ the
+ * user uses extended colour.
*/
- if (c->attr < 0x8000) {
- add(b, (unsigned char)((c->attr >> 8) & 0xFF));
- add(b, (unsigned char)(c->attr & 0xFF));
+ unsigned attr, colourbits;
+
+ attr = c->attr;
+
+ assert(ATTR_BGSHIFT > ATTR_FGSHIFT);
+
+ colourbits = (attr >> (ATTR_BGSHIFT + 4)) & 0xF;
+ colourbits <<= 4;
+ colourbits |= (attr >> (ATTR_FGSHIFT + 4)) & 0xF;
+
+ attr = (((attr >> (ATTR_BGSHIFT + 8)) << (ATTR_BGSHIFT + 4)) |
+ (attr & ((1 << (ATTR_BGSHIFT + 4))-1)));
+ attr = (((attr >> (ATTR_FGSHIFT + 8)) << (ATTR_FGSHIFT + 4)) |
+ (attr & ((1 << (ATTR_FGSHIFT + 4))-1)));
+
+ attr |= (colourbits << (32-9));
+
+ if (attr < 0x8000) {
+ add(b, (unsigned char)((attr >> 8) & 0xFF));
+ add(b, (unsigned char)(attr & 0xFF));
} else {
- add(b, (unsigned char)(((c->attr >> 24) & 0x7F) | 0x80));
- add(b, (unsigned char)((c->attr >> 16) & 0xFF));
- add(b, (unsigned char)((c->attr >> 8) & 0xFF));
- add(b, (unsigned char)(c->attr & 0xFF));
+ add(b, (unsigned char)(((attr >> 24) & 0x7F) | 0x80));
+ add(b, (unsigned char)((attr >> 16) & 0xFF));
+ add(b, (unsigned char)((attr >> 8) & 0xFF));
+ add(b, (unsigned char)(attr & 0xFF));
}
}
static void makeliteral_cc(struct buf *b, termchar *c, unsigned long *state)
static void readliteral_attr(struct buf *b, termchar *c, termline *ldata,
unsigned long *state)
{
- int val;
+ unsigned val, attr, colourbits;
val = get(b) << 8;
val |= get(b);
if (val >= 0x8000) {
+ val &= ~0x8000;
val <<= 16;
val |= get(b) << 8;
val |= get(b);
}
- c->attr = val;
+ colourbits = (val >> (32-9)) & 0xFF;
+ attr = (val & ((1<<(32-9))-1));
+
+ attr = (((attr >> (ATTR_FGSHIFT + 4)) << (ATTR_FGSHIFT + 8)) |
+ (attr & ((1 << (ATTR_FGSHIFT + 4))-1)));
+ attr = (((attr >> (ATTR_BGSHIFT + 4)) << (ATTR_BGSHIFT + 8)) |
+ (attr & ((1 << (ATTR_BGSHIFT + 4))-1)));
+
+ attr |= (colourbits >> 4) << (ATTR_BGSHIFT + 4);
+ attr |= (colourbits & 0xF) << (ATTR_FGSHIFT + 4);
+
+ c->attr = attr;
}
static void readliteral_cc(struct buf *b, termchar *c, termline *ldata,
unsigned long *state)
term_update(term);
}
+static void term_schedule_update(Terminal *term)
+{
+ if (!term->window_update_pending) {
+ term->window_update_pending = TRUE;
+ term->next_update = schedule_timer(UPDATE_DELAY, term_timer, term);
+ }
+}
+
/*
* Call this whenever the terminal window state changes, to queue
* an update.
static void seen_disp_event(Terminal *term)
{
term->seen_disp_event = TRUE; /* for scrollback-reset-on-activity */
- if (!term->window_update_pending) {
- term->window_update_pending = TRUE;
- term->next_update = schedule_timer(UPDATE_DELAY, term_timer, term);
- }
+ term_schedule_update(term);
}
/*
*/
static void term_schedule_tblink(Terminal *term)
{
- if (term->tblink_pending)
- return; /* already well in hand */
if (term->blink_is_real) {
- term->next_tblink = schedule_timer(TBLINK_DELAY, term_timer, term);
+ if (!term->tblink_pending)
+ term->next_tblink = schedule_timer(TBLINK_DELAY, term_timer, term);
term->tblink_pending = TRUE;
} else {
term->tblinker = 1; /* reset when not in use */
*/
static void term_schedule_cblink(Terminal *term)
{
- if (term->cblink_pending)
- return; /* already well in hand */
- if (term->cfg.blink_cur) {
- term->next_cblink = schedule_timer(CBLINK_DELAY, term_timer, term);
+ if (term->cfg.blink_cur && term->has_focus) {
+ if (!term->cblink_pending)
+ term->next_cblink = schedule_timer(CBLINK_DELAY, term_timer, term);
term->cblink_pending = TRUE;
} else {
term->cblinker = 1; /* reset when not in use */
/* xterm-style bright foreground */
term->curr_attr &= ~ATTR_FGMASK;
term->curr_attr |=
- ((term->esc_args[i] - 90 + 16)
+ ((term->esc_args[i] - 90 + 8)
<< ATTR_FGSHIFT);
break;
case 39: /* default-foreground */
/* xterm-style bright background */
term->curr_attr &= ~ATTR_BGMASK;
term->curr_attr |=
- ((term->esc_args[i] - 100 + 16)
+ ((term->esc_args[i] - 100 + 8)
<< ATTR_BGSHIFT);
break;
case 49: /* default-background */
term->curr_attr &= ~ATTR_BGMASK;
term->curr_attr |= ATTR_DEFBG;
break;
+ case 38: /* xterm 256-colour mode */
+ if (i+2 < term->esc_nargs &&
+ term->esc_args[i+1] == 5) {
+ term->curr_attr &= ~ATTR_FGMASK;
+ term->curr_attr |=
+ ((term->esc_args[i+2] & 0xFF)
+ << ATTR_FGSHIFT);
+ i += 2;
+ }
+ break;
+ case 48: /* xterm 256-colour mode */
+ if (i+2 < term->esc_nargs &&
+ term->esc_args[i+1] == 5) {
+ term->curr_attr &= ~ATTR_BGMASK;
+ term->curr_attr |=
+ ((term->esc_args[i+2] & 0xFF)
+ << ATTR_BGSHIFT);
+ i += 2;
+ }
+ break;
}
}
set_erase_char(term);
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)) <<
+ (term->esc_args[0] & 0x8)) <<
ATTR_FGSHIFT;
term->curr_attr &= ~ATTR_FGMASK;
term->curr_attr |= colour;
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)) <<
+ (term->esc_args[0] & 0x8)) <<
ATTR_BGSHIFT;
term->curr_attr &= ~ATTR_BGMASK;
term->curr_attr |= colour;
term->termstate = TOPLEVEL;
term->curr_attr &= ~ATTR_FGMASK;
term->curr_attr &= ~ATTR_BOLD;
- term->curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
- if ((c & 0x8) || term->vt52_bold)
- term->curr_attr |= ATTR_BOLD;
-
+ term->curr_attr |= (c & 0xF) << ATTR_FGSHIFT;
set_erase_char(term);
break;
case VT52_BG:
term->termstate = TOPLEVEL;
term->curr_attr &= ~ATTR_BGMASK;
term->curr_attr &= ~ATTR_BLINK;
- term->curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
-
- /* Note: bold background */
- if (c & 0x8)
- term->curr_attr |= ATTR_BLINK;
-
+ term->curr_attr |= (c & 0xF) << ATTR_BGSHIFT;
set_erase_char(term);
break;
#endif
}
term_print_flush(term);
- logflush(term->logctx);
+ if (term->cfg.logflush)
+ logflush(term->logctx);
}
/*
}
static void term_bidi_cache_store(Terminal *term, int line, termchar *lbefore,
- termchar *lafter, int width)
+ termchar *lafter, bidi_char *wcTo,
+ int width, int size)
{
+ int i;
+
if (!term->pre_bidi_cache || term->bidi_cache_size <= line) {
int j = term->bidi_cache_size;
term->bidi_cache_size = line+1;
term->post_bidi_cache[j].chars = NULL;
term->pre_bidi_cache[j].width =
term->post_bidi_cache[j].width = -1;
+ term->pre_bidi_cache[j].forward =
+ term->post_bidi_cache[j].forward = NULL;
+ term->pre_bidi_cache[j].backward =
+ term->post_bidi_cache[j].backward = NULL;
j++;
}
}
sfree(term->pre_bidi_cache[line].chars);
sfree(term->post_bidi_cache[line].chars);
+ sfree(term->post_bidi_cache[line].forward);
+ sfree(term->post_bidi_cache[line].backward);
term->pre_bidi_cache[line].width = width;
- term->pre_bidi_cache[line].chars = snewn(width, termchar);
+ term->pre_bidi_cache[line].chars = snewn(size, termchar);
term->post_bidi_cache[line].width = width;
- term->post_bidi_cache[line].chars = snewn(width, termchar);
+ term->post_bidi_cache[line].chars = snewn(size, 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, size * TSIZE);
+ memcpy(term->post_bidi_cache[line].chars, lafter, size * 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;
+ }
+}
+
+/*
+ * Prepare the bidi information for a screen line. Returns the
+ * 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 termchar *term_bidi_line(Terminal *term, struct termline *ldata,
+ int scr_y)
+{
+ termchar *lchars;
+ int it;
+
+ /* Do Arabic shaping and bidi. */
+ if(!term->cfg.bidi || !term->cfg.arabicshaping) {
+
+ if (!term_bidi_cache_hit(term, scr_y, ldata->chars, term->cols)) {
+
+ if (term->wcFromTo_size < term->cols) {
+ term->wcFromTo_size = term->cols;
+ term->wcFrom = sresize(term->wcFrom, term->wcFromTo_size,
+ bidi_char);
+ term->wcTo = sresize(term->wcTo, term->wcFromTo_size,
+ bidi_char);
+ }
+
+ for(it=0; it<term->cols ; it++)
+ {
+ unsigned long uc = (ldata->chars[it].chr);
+
+ switch (uc & CSET_MASK) {
+ case CSET_LINEDRW:
+ if (!term->cfg.rawcnp) {
+ uc = term->ucsdata->unitab_xterm[uc & 0xFF];
+ break;
+ }
+ case CSET_ASCII:
+ uc = term->ucsdata->unitab_line[uc & 0xFF];
+ break;
+ case CSET_SCOACS:
+ uc = term->ucsdata->unitab_scoacs[uc&0xFF];
+ break;
+ }
+ switch (uc & CSET_MASK) {
+ case CSET_ACP:
+ uc = term->ucsdata->unitab_font[uc & 0xFF];
+ break;
+ case CSET_OEMCP:
+ uc = term->ucsdata->unitab_oemcp[uc & 0xFF];
+ break;
+ }
+
+ term->wcFrom[it].origwc = term->wcFrom[it].wc =
+ (wchar_t)uc;
+ term->wcFrom[it].index = it;
+ }
+
+ if(!term->cfg.bidi)
+ do_bidi(term->wcFrom, term->cols);
+
+ /* this is saved iff done from inside the shaping */
+ if(!term->cfg.bidi && term->cfg.arabicshaping)
+ for(it=0; it<term->cols; it++)
+ term->wcTo[it] = term->wcFrom[it];
- memcpy(term->pre_bidi_cache[line].chars, lbefore, width * TSIZE);
- memcpy(term->post_bidi_cache[line].chars, lafter, width * TSIZE);
+ if(!term->cfg.arabicshaping)
+ do_shape(term->wcFrom, term->wcTo, term->cols);
+
+ if (term->ltemp_size < ldata->size) {
+ term->ltemp_size = ldata->size;
+ term->ltemp = sresize(term->ltemp, term->ltemp_size,
+ termchar);
+ }
+
+ memcpy(term->ltemp, ldata->chars, ldata->size * TSIZE);
+
+ for(it=0; it<term->cols ; it++)
+ {
+ term->ltemp[it] = ldata->chars[term->wcTo[it].index];
+ if (term->ltemp[it].cc_next)
+ term->ltemp[it].cc_next -=
+ it - term->wcTo[it].index;
+
+ if (term->wcTo[it].origwc != term->wcTo[it].wc)
+ term->ltemp[it].chr = term->wcTo[it].wc;
+ }
+ term_bidi_cache_store(term, scr_y, ldata->chars,
+ term->ltemp, term->wcTo,
+ term->cols, ldata->size);
+
+ lchars = term->ltemp;
+ } else {
+ lchars = term->post_bidi_cache[scr_y].chars;
+ }
+ } else {
+ lchars = NULL;
+ }
+
+ return lchars;
}
/*
*/
static void do_paint(Terminal *term, Context ctx, int may_optimise)
{
- int i, it, j, our_curs_y, our_curs_x;
+ int i, j, our_curs_y, our_curs_x;
int rv, cursor;
pos scrpos;
wchar_t *ch;
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.
+ * 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
+ lchars = ldata->chars;
+
if (our_curs_x > 0 &&
- ldata->chars[our_curs_x].chr == UCSWIDE)
+ lchars[our_curs_x].chr == UCSWIDE)
our_curs_x--;
+
unlineptr(ldata);
}
int start = 0;
int ccount = 0;
int last_run_dirty = 0;
+ int *backward;
scrpos.y = i + term->disptop;
ldata = lineptr(scrpos.y);
term->disptext[i]->lattr = ldata->lattr;
/* Do Arabic shaping and bidi. */
- if(!term->cfg.bidi || !term->cfg.arabicshaping) {
-
- if (!term_bidi_cache_hit(term, i, ldata->chars, term->cols)) {
-
- if (term->wcFromTo_size < term->cols) {
- term->wcFromTo_size = term->cols;
- term->wcFrom = sresize(term->wcFrom, term->wcFromTo_size,
- bidi_char);
- term->wcTo = sresize(term->wcTo, term->wcFromTo_size,
- bidi_char);
- }
-
- for(it=0; it<term->cols ; it++)
- {
- unsigned long uc = (ldata->chars[it].chr);
-
- switch (uc & CSET_MASK) {
- case CSET_LINEDRW:
- if (!term->cfg.rawcnp) {
- uc = term->ucsdata->unitab_xterm[uc & 0xFF];
- break;
- }
- case CSET_ASCII:
- uc = term->ucsdata->unitab_line[uc & 0xFF];
- break;
- case CSET_SCOACS:
- uc = term->ucsdata->unitab_scoacs[uc&0xFF];
- break;
- }
- switch (uc & CSET_MASK) {
- case CSET_ACP:
- uc = term->ucsdata->unitab_font[uc & 0xFF];
- break;
- case CSET_OEMCP:
- uc = term->ucsdata->unitab_oemcp[uc & 0xFF];
- break;
- }
-
- term->wcFrom[it].origwc = term->wcFrom[it].wc =
- (wchar_t)uc;
- term->wcFrom[it].index = it;
- }
-
- if(!term->cfg.bidi)
- do_bidi(term->wcFrom, term->cols);
-
- /* this is saved iff done from inside the shaping */
- if(!term->cfg.bidi && term->cfg.arabicshaping)
- for(it=0; it<term->cols; it++)
- term->wcTo[it] = term->wcFrom[it];
-
- if(!term->cfg.arabicshaping)
- do_shape(term->wcFrom, term->wcTo, term->cols);
-
- if (term->ltemp_size < ldata->size) {
- term->ltemp_size = ldata->size;
- term->ltemp = sresize(term->ltemp, term->ltemp_size,
- termchar);
- }
-
- memcpy(term->ltemp, ldata->chars, ldata->size * TSIZE);
-
- for(it=0; it<term->cols ; it++)
- {
- term->ltemp[it] = ldata->chars[term->wcTo[it].index];
- if (term->ltemp[it].cc_next)
- term->ltemp[it].cc_next -=
- it - term->wcTo[it].index;
-
- if (term->wcTo[it].origwc != term->wcTo[it].wc)
- term->ltemp[it].chr = term->wcTo[it].wc;
- }
- term_bidi_cache_store(term, i, ldata->chars,
- term->ltemp, ldata->size);
-
- lchars = term->ltemp;
- } else {
- lchars = term->post_bidi_cache[i].chars;
- }
- } else
+ 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++) {
unsigned long tattr, tchar;
termchar *d = lchars + j;
int break_run, do_copy;
- scrpos.x = j;
+ scrpos.x = backward ? backward[j] : j;
tchar = d->chr;
tattr = d->attr;
tattr = (tattr & ~(ATTR_FGMASK | ATTR_BGMASK)) |
ATTR_DEFFG | ATTR_DEFBG;
+ if (!term->cfg.xterm_256_colour) {
+ int colour;
+ colour = (tattr & ATTR_FGMASK) >> ATTR_FGSHIFT;
+ if (colour >= 16 && colour < 256)
+ tattr = (tattr &~ ATTR_FGMASK) | ATTR_DEFFG;
+ colour = (tattr & ATTR_BGMASK) >> ATTR_BGSHIFT;
+ if (colour >= 16 && colour < 256)
+ tattr = (tattr &~ ATTR_BGMASK) | ATTR_DEFBG;
+ }
+
switch (tchar & CSET_MASK) {
case CSET_ASCII:
tchar = term->ucsdata->unitab_line[tchar & 0xFF];
for (i = 0; i < term->rows; i++)
for (j = 0; j < term->cols; j++)
term->disptext[i]->chars[j].attr = ATTR_INVALID;
+
+ term_schedule_update(term);
}
/*
if (immediately) {
do_paint (term, ctx, FALSE);
} else {
- if (!term->window_update_pending) {
- term->window_update_pending = TRUE;
- term->next_update = schedule_timer(UPDATE_DELAY, term_timer, term);
- }
+ term_schedule_update(term);
}
}
x = term->cols - 1;
selpoint.y = y + term->disptop;
- selpoint.x = x;
ldata = lineptr(selpoint.y);
+
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);
if (raw_mouse) {
{
term->logctx = logctx;
}
+
+void term_set_focus(Terminal *term, int has_focus)
+{
+ term->has_focus = has_focus;
+ term_schedule_cblink(term);
+}