mirror of
https://github.com/tmux/tmux.git
synced 2026-02-13 17:49:15 +00:00
Replace overlay_ranges with visible_ranges which can hold more than
three ranges (will be needed for floating panes); move the visible ranges checks outside of tty_draw_line and rewrite it to fix issues with partially-obscured wide characters. With Michael Grant.
This commit is contained in:
281
tty.c
281
tty.c
@@ -61,18 +61,11 @@ static void tty_region(struct tty *, u_int, u_int);
|
||||
static void tty_margin_pane(struct tty *, const struct tty_ctx *);
|
||||
static void tty_margin(struct tty *, u_int, u_int);
|
||||
static int tty_large_region(struct tty *, const struct tty_ctx *);
|
||||
static int tty_fake_bce(const struct tty *, const struct grid_cell *,
|
||||
u_int);
|
||||
static void tty_redraw_region(struct tty *, const struct tty_ctx *);
|
||||
static void tty_emulate_repeat(struct tty *, enum tty_code_code,
|
||||
enum tty_code_code, u_int);
|
||||
static void tty_repeat_space(struct tty *, u_int);
|
||||
static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int);
|
||||
static void tty_default_attributes(struct tty *, const struct grid_cell *,
|
||||
struct colour_palette *, u_int, struct hyperlinks *);
|
||||
static int tty_check_overlay(struct tty *, u_int, u_int);
|
||||
static void tty_check_overlay_range(struct tty *, u_int, u_int, u_int,
|
||||
struct overlay_ranges *);
|
||||
|
||||
#define tty_use_margin(tty) \
|
||||
(tty->term->flags & TERM_DECSLRM)
|
||||
@@ -523,6 +516,8 @@ void
|
||||
tty_free(struct tty *tty)
|
||||
{
|
||||
tty_close(tty);
|
||||
|
||||
free(tty->r.ranges);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -907,7 +902,7 @@ tty_emulate_repeat(struct tty *tty, enum tty_code_code code,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
tty_repeat_space(struct tty *tty, u_int n)
|
||||
{
|
||||
static char s[500];
|
||||
@@ -1066,7 +1061,7 @@ tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx)
|
||||
* Return if BCE is needed but the terminal doesn't have it - it'll need to be
|
||||
* emulated.
|
||||
*/
|
||||
static int
|
||||
int
|
||||
tty_fake_bce(const struct tty *tty, const struct grid_cell *gc, u_int bg)
|
||||
{
|
||||
if (tty_term_flag(tty->term, TTYC_BCE))
|
||||
@@ -1161,8 +1156,6 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py,
|
||||
u_int px, u_int nx, u_int bg)
|
||||
{
|
||||
struct client *c = tty->client;
|
||||
struct overlay_ranges r;
|
||||
u_int i;
|
||||
|
||||
log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
|
||||
|
||||
@@ -1198,13 +1191,8 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py,
|
||||
* Couldn't use an escape sequence, use spaces. Clear only the visible
|
||||
* bit if there is an overlay.
|
||||
*/
|
||||
tty_check_overlay_range(tty, px, py, nx, &r);
|
||||
for (i = 0; i < OVERLAY_MAX_RANGES; i++) {
|
||||
if (r.nx[i] == 0)
|
||||
continue;
|
||||
tty_cursor(tty, r.px[i], py);
|
||||
tty_repeat_space(tty, r.nx[i]);
|
||||
}
|
||||
tty_cursor(tty, px, py);
|
||||
tty_repeat_space(tty, nx);
|
||||
}
|
||||
|
||||
/* Clear a line, adjusting to visible part of pane. */
|
||||
@@ -1385,7 +1373,7 @@ tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py)
|
||||
}
|
||||
}
|
||||
|
||||
static const struct grid_cell *
|
||||
const struct grid_cell *
|
||||
tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
|
||||
{
|
||||
static struct grid_cell new;
|
||||
@@ -1418,229 +1406,34 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
|
||||
return (&new);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if a single character is obstructed by the overlay and return a
|
||||
* boolean.
|
||||
*/
|
||||
/* Check if a single character is covered by the overlay. */
|
||||
static int
|
||||
tty_check_overlay(struct tty *tty, u_int px, u_int py)
|
||||
{
|
||||
struct overlay_ranges r;
|
||||
struct visible_ranges *r;
|
||||
|
||||
/*
|
||||
* A unit width range will always return nx[2] == 0 from a check, even
|
||||
* with multiple overlays, so it's sufficient to check just the first
|
||||
* two entries.
|
||||
* With a single character, if there is anything visible (that is, the
|
||||
* range is not empty), it must be that character.
|
||||
*/
|
||||
tty_check_overlay_range(tty, px, py, 1, &r);
|
||||
if (r.nx[0] + r.nx[1] == 0)
|
||||
return (0);
|
||||
return (1);
|
||||
r = tty_check_overlay_range(tty, px, py, 1);
|
||||
return (!server_client_ranges_is_empty(r));
|
||||
}
|
||||
|
||||
/* Return parts of the input range which are visible. */
|
||||
static void
|
||||
tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx,
|
||||
struct overlay_ranges *r)
|
||||
struct visible_ranges *
|
||||
tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx)
|
||||
{
|
||||
struct client *c = tty->client;
|
||||
|
||||
if (c->overlay_check == NULL) {
|
||||
r->px[0] = px;
|
||||
r->nx[0] = nx;
|
||||
r->px[1] = 0;
|
||||
r->nx[1] = 0;
|
||||
r->px[2] = 0;
|
||||
r->nx[2] = 0;
|
||||
return;
|
||||
server_client_ensure_ranges(&tty->r, 1);
|
||||
tty->r.ranges[0].px = px;
|
||||
tty->r.ranges[0].nx = nx;
|
||||
tty->r.used = 1;
|
||||
return (&tty->r);
|
||||
}
|
||||
|
||||
c->overlay_check(c, c->overlay_data, px, py, nx, r);
|
||||
}
|
||||
|
||||
void
|
||||
tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
|
||||
u_int atx, u_int aty, const struct grid_cell *defaults,
|
||||
struct colour_palette *palette)
|
||||
{
|
||||
struct grid *gd = s->grid;
|
||||
struct grid_cell gc, last;
|
||||
const struct grid_cell *gcp;
|
||||
struct grid_line *gl;
|
||||
struct client *c = tty->client;
|
||||
struct overlay_ranges r;
|
||||
u_int i, j, ux, sx, width, hidden, eux, nxx;
|
||||
u_int cellsize;
|
||||
int flags, cleared = 0, wrapped = 0;
|
||||
char buf[512];
|
||||
size_t len;
|
||||
|
||||
log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__,
|
||||
px, py, nx, atx, aty);
|
||||
log_debug("%s: defaults: fg=%d, bg=%d", __func__, defaults->fg,
|
||||
defaults->bg);
|
||||
|
||||
/*
|
||||
* py is the line in the screen to draw.
|
||||
* px is the start x and nx is the width to draw.
|
||||
* atx,aty is the line on the terminal to draw it.
|
||||
*/
|
||||
|
||||
flags = (tty->flags & TTY_NOCURSOR);
|
||||
tty->flags |= TTY_NOCURSOR;
|
||||
tty_update_mode(tty, tty->mode, s);
|
||||
|
||||
tty_region_off(tty);
|
||||
tty_margin_off(tty);
|
||||
|
||||
/*
|
||||
* Clamp the width to cellsize - note this is not cellused, because
|
||||
* there may be empty background cells after it (from BCE).
|
||||
*/
|
||||
sx = screen_size_x(s);
|
||||
if (nx > sx)
|
||||
nx = sx;
|
||||
cellsize = grid_get_line(gd, gd->hsize + py)->cellsize;
|
||||
if (sx > cellsize)
|
||||
sx = cellsize;
|
||||
if (sx > tty->sx)
|
||||
sx = tty->sx;
|
||||
if (sx > nx)
|
||||
sx = nx;
|
||||
ux = 0;
|
||||
|
||||
if (py == 0)
|
||||
gl = NULL;
|
||||
else
|
||||
gl = grid_get_line(gd, gd->hsize + py - 1);
|
||||
if (gl == NULL ||
|
||||
(~gl->flags & GRID_LINE_WRAPPED) ||
|
||||
atx != 0 ||
|
||||
tty->cx < tty->sx ||
|
||||
nx < tty->sx) {
|
||||
if (nx < tty->sx &&
|
||||
atx == 0 &&
|
||||
px + sx != nx &&
|
||||
tty_term_has(tty->term, TTYC_EL1) &&
|
||||
!tty_fake_bce(tty, defaults, 8) &&
|
||||
c->overlay_check == NULL) {
|
||||
tty_default_attributes(tty, defaults, palette, 8,
|
||||
s->hyperlinks);
|
||||
tty_cursor(tty, nx - 1, aty);
|
||||
tty_putcode(tty, TTYC_EL1);
|
||||
cleared = 1;
|
||||
}
|
||||
} else {
|
||||
log_debug("%s: wrapped line %u", __func__, aty);
|
||||
wrapped = 1;
|
||||
}
|
||||
|
||||
memcpy(&last, &grid_default_cell, sizeof last);
|
||||
len = 0;
|
||||
width = 0;
|
||||
|
||||
for (i = 0; i < sx; i++) {
|
||||
grid_view_get_cell(gd, px + i, py, &gc);
|
||||
gcp = tty_check_codeset(tty, &gc);
|
||||
if (len != 0 &&
|
||||
(!tty_check_overlay(tty, atx + ux + width, aty) ||
|
||||
(gcp->attr & GRID_ATTR_CHARSET) ||
|
||||
gcp->flags != last.flags ||
|
||||
gcp->attr != last.attr ||
|
||||
gcp->fg != last.fg ||
|
||||
gcp->bg != last.bg ||
|
||||
gcp->us != last.us ||
|
||||
gcp->link != last.link ||
|
||||
ux + width + gcp->data.width > nx ||
|
||||
(sizeof buf) - len < gcp->data.size)) {
|
||||
tty_attributes(tty, &last, defaults, palette,
|
||||
s->hyperlinks);
|
||||
if (last.flags & GRID_FLAG_CLEARED) {
|
||||
log_debug("%s: %zu cleared", __func__, len);
|
||||
tty_clear_line(tty, defaults, aty, atx + ux,
|
||||
width, last.bg);
|
||||
} else {
|
||||
if (!wrapped || atx != 0 || ux != 0)
|
||||
tty_cursor(tty, atx + ux, aty);
|
||||
tty_putn(tty, buf, len, width);
|
||||
}
|
||||
ux += width;
|
||||
|
||||
len = 0;
|
||||
width = 0;
|
||||
wrapped = 0;
|
||||
}
|
||||
|
||||
if (gcp->flags & GRID_FLAG_SELECTED)
|
||||
screen_select_cell(s, &last, gcp);
|
||||
else
|
||||
memcpy(&last, gcp, sizeof last);
|
||||
|
||||
tty_check_overlay_range(tty, atx + ux, aty, gcp->data.width,
|
||||
&r);
|
||||
hidden = 0;
|
||||
for (j = 0; j < OVERLAY_MAX_RANGES; j++)
|
||||
hidden += r.nx[j];
|
||||
hidden = gcp->data.width - hidden;
|
||||
if (hidden != 0 && hidden == gcp->data.width) {
|
||||
if (~gcp->flags & GRID_FLAG_PADDING)
|
||||
ux += gcp->data.width;
|
||||
} else if (hidden != 0 || ux + gcp->data.width > nx) {
|
||||
if (~gcp->flags & GRID_FLAG_PADDING) {
|
||||
tty_attributes(tty, &last, defaults, palette,
|
||||
s->hyperlinks);
|
||||
for (j = 0; j < OVERLAY_MAX_RANGES; j++) {
|
||||
if (r.nx[j] == 0)
|
||||
continue;
|
||||
/* Effective width drawn so far. */
|
||||
eux = r.px[j] - atx;
|
||||
if (eux < nx) {
|
||||
tty_cursor(tty, r.px[j], aty);
|
||||
nxx = nx - eux;
|
||||
if (r.nx[j] > nxx)
|
||||
r.nx[j] = nxx;
|
||||
tty_repeat_space(tty, r.nx[j]);
|
||||
ux = eux + r.nx[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (gcp->attr & GRID_ATTR_CHARSET) {
|
||||
tty_attributes(tty, &last, defaults, palette,
|
||||
s->hyperlinks);
|
||||
tty_cursor(tty, atx + ux, aty);
|
||||
for (j = 0; j < gcp->data.size; j++)
|
||||
tty_putc(tty, gcp->data.data[j]);
|
||||
ux += gcp->data.width;
|
||||
} else if (~gcp->flags & GRID_FLAG_PADDING) {
|
||||
memcpy(buf + len, gcp->data.data, gcp->data.size);
|
||||
len += gcp->data.size;
|
||||
width += gcp->data.width;
|
||||
}
|
||||
}
|
||||
if (len != 0 && ((~last.flags & GRID_FLAG_CLEARED) || last.bg != 8)) {
|
||||
tty_attributes(tty, &last, defaults, palette, s->hyperlinks);
|
||||
if (last.flags & GRID_FLAG_CLEARED) {
|
||||
log_debug("%s: %zu cleared (end)", __func__, len);
|
||||
tty_clear_line(tty, defaults, aty, atx + ux, width,
|
||||
last.bg);
|
||||
} else {
|
||||
if (!wrapped || atx != 0 || ux != 0)
|
||||
tty_cursor(tty, atx + ux, aty);
|
||||
tty_putn(tty, buf, len, width);
|
||||
}
|
||||
ux += width;
|
||||
}
|
||||
|
||||
if (!cleared && ux < nx) {
|
||||
log_debug("%s: %u to end of line (%zu cleared)", __func__,
|
||||
nx - ux, len);
|
||||
tty_default_attributes(tty, defaults, palette, 8,
|
||||
s->hyperlinks);
|
||||
tty_clear_line(tty, defaults, aty, atx + ux, nx - ux, 8);
|
||||
}
|
||||
|
||||
tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags;
|
||||
tty_update_mode(tty, tty->mode, s);
|
||||
return (c->overlay_check(c, c->overlay_data, px, py, nx));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2103,7 +1896,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
|
||||
{
|
||||
const struct grid_cell *gcp = ctx->cell;
|
||||
struct screen *s = ctx->s;
|
||||
struct overlay_ranges r;
|
||||
struct visible_ranges *r;
|
||||
u_int px, py, i, vis = 0;
|
||||
|
||||
px = ctx->xoff + ctx->ocx - ctx->wox;
|
||||
@@ -2120,9 +1913,9 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
|
||||
|
||||
/* Handle partially obstructed wide characters. */
|
||||
if (gcp->data.width > 1) {
|
||||
tty_check_overlay_range(tty, px, py, gcp->data.width, &r);
|
||||
for (i = 0; i < OVERLAY_MAX_RANGES; i++)
|
||||
vis += r.nx[i];
|
||||
r = tty_check_overlay_range(tty, px, py, gcp->data.width);
|
||||
for (i = 0; i < r->used; i++)
|
||||
vis += r->ranges[i].nx;
|
||||
if (vis < gcp->data.width) {
|
||||
tty_draw_line(tty, s, s->cx, s->cy, gcp->data.width,
|
||||
px, py, &ctx->defaults, ctx->palette);
|
||||
@@ -2148,7 +1941,8 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
|
||||
void
|
||||
tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
|
||||
{
|
||||
struct overlay_ranges r;
|
||||
struct visible_ranges *r;
|
||||
struct visible_range *rr;
|
||||
u_int i, px, py, cx;
|
||||
char *cp = ctx->ptr;
|
||||
|
||||
@@ -2173,20 +1967,21 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
|
||||
|
||||
tty_margin_off(tty);
|
||||
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
|
||||
tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette, ctx->s->hyperlinks);
|
||||
tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette,
|
||||
ctx->s->hyperlinks);
|
||||
|
||||
/* Get tty position from pane position for overlay check. */
|
||||
px = ctx->xoff + ctx->ocx - ctx->wox;
|
||||
py = ctx->yoff + ctx->ocy - ctx->woy;
|
||||
|
||||
tty_check_overlay_range(tty, px, py, ctx->num, &r);
|
||||
for (i = 0; i < OVERLAY_MAX_RANGES; i++) {
|
||||
if (r.nx[i] == 0)
|
||||
continue;
|
||||
/* Convert back to pane position for printing. */
|
||||
cx = r.px[i] - ctx->xoff + ctx->wox;
|
||||
tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy);
|
||||
tty_putn(tty, cp + r.px[i] - px, r.nx[i], r.nx[i]);
|
||||
r = tty_check_overlay_range(tty, px, py, ctx->num);
|
||||
for (i = 0; i < r->used; i++) {
|
||||
rr = &r->ranges[i];
|
||||
if (rr->nx != 0) {
|
||||
cx = rr->px - ctx->xoff + ctx->wox;
|
||||
tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy);
|
||||
tty_putn(tty, cp + rr->px - px, rr->nx, rr->nx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3081,7 +2876,7 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
tty_default_attributes(struct tty *tty, const struct grid_cell *defaults,
|
||||
struct colour_palette *palette, u_int bg, struct hyperlinks *hl)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user