Merge branch 'obsd-master'

This commit is contained in:
Thomas Adam
2026-01-28 17:25:42 +00:00
10 changed files with 502 additions and 316 deletions

View File

@@ -196,6 +196,7 @@ dist_tmux_SOURCES = \
tmux.h \
tmux-protocol.h \
tty-acs.c \
tty-draw.c \
tty-features.c \
tty-keys.c \
tty-term.c \

2
grid.c
View File

@@ -235,7 +235,7 @@ grid_check_y(struct grid *gd, const char *from, u_int py)
int
grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
{
int flags1 = gc1->flags, flags2 = gc2->flags;;
int flags1 = gc1->flags, flags2 = gc2->flags;
if (gc1->fg != gc2->fg || gc1->bg != gc2->bg)
return (0);

10
menu.c
View File

@@ -38,6 +38,7 @@ struct menu_data {
struct cmd_find_state fs;
struct screen s;
struct visible_ranges r;
u_int px;
u_int py;
@@ -185,15 +186,16 @@ menu_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
}
/* Return parts of the input range which are not obstructed by the menu. */
void
struct visible_ranges *
menu_check_cb(__unused struct client *c, void *data, u_int px, u_int py,
u_int nx, struct overlay_ranges *r)
u_int nx)
{
struct menu_data *md = data;
struct menu *menu = md->menu;
server_client_overlay_range(md->px, md->py, menu->width + 4,
menu->count + 2, px, py, nx, r);
menu->count + 2, px, py, nx, &md->r);
return (&md->r);
}
static void
@@ -292,7 +294,9 @@ menu_free_cb(__unused struct client *c, void *data)
if (md->cb != NULL)
md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data);
free(md->r.ranges);
screen_free(&md->s);
menu_free(md->menu);
free(md->style);
free(md->selected_style);

67
popup.c
View File

@@ -41,6 +41,9 @@ struct popup_data {
struct grid_cell defaults;
struct colour_palette palette;
struct visible_ranges r;
struct visible_ranges or[2];
struct job *job;
struct input_ctx *ictx;
int status;
@@ -209,48 +212,57 @@ popup_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
}
/* Return parts of the input range which are not obstructed by the popup. */
static void
popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx,
struct overlay_ranges *r)
static struct visible_ranges *
popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx)
{
struct popup_data *pd = data;
struct overlay_ranges or[2];
struct visible_ranges *r = &pd->r;
struct visible_ranges *mr;
u_int i, j, k = 0;
if (pd->md != NULL) {
/* Check each returned range for the menu against the popup. */
menu_check_cb(c, pd->md, px, py, nx, r);
for (i = 0; i < 2; i++) {
/*
* Work out the visible ranges for the menu (that is, the
* ranges not covered by the menu). A menu should have at most
* two ranges and we rely on this being the case.
*/
mr = menu_check_cb(c, pd->md, px, py, nx);
if (mr->used > 2)
fatalx("too many menu ranges");
/*
* Walk the ranges still visible under the menu and check if
* each is visible under the popup as well. At most there can be
* three total ranges if popup and menu do not intersect.
*/
for (i = 0; i < mr->used; i++) {
server_client_overlay_range(pd->px, pd->py, pd->sx,
pd->sy, r->px[i], py, r->nx[i], &or[i]);
pd->sy, r->ranges[i].px, py, r->ranges[i].nx,
&pd->or[i]);
}
/*
* or has up to OVERLAY_MAX_RANGES non-overlapping ranges,
* ordered from left to right. Collect them in the output.
* We now have nonoverlapping ranges from left to right.
* Combine them together into the output.
*/
for (i = 0; i < 2; i++) {
/* Each or[i] only has 2 ranges. */
for (j = 0; j < 2; j++) {
if (or[i].nx[j] > 0) {
r->px[k] = or[i].px[j];
r->nx[k] = or[i].nx[j];
k++;
}
server_client_ensure_ranges(r, 3);
for (i = 0; i < mr->used; i++) {
for (j = 0; j < pd->or[i].used; j++) {
if (pd->or[i].ranges[j].nx == 0)
continue;
if (k >= 3)
fatalx("too many popup & menu ranges");
r->ranges[k].px = pd->or[i].ranges[j].px;
r->ranges[k].nx = pd->or[i].ranges[j].nx;
k++;
}
}
/* Zero remaining ranges if any. */
for (i = k; i < OVERLAY_MAX_RANGES; i++) {
r->px[i] = 0;
r->nx[i] = 0;
}
return;
return (r);
}
server_client_overlay_range(pd->px, pd->py, pd->sx, pd->sy, px, py, nx,
r);
return (r);
}
static void
@@ -333,6 +345,9 @@ popup_free_cb(struct client *c, void *data)
job_free(pd->job);
input_free(pd->ictx);
free(pd->or[0].ranges);
free(pd->or[1].ranges);
free(pd->r.ranges);
screen_free(&pd->s);
colour_palette_free(&pd->palette);

View File

@@ -817,13 +817,14 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
struct window_pane *wp, *active = server_client_get_pane(c);
struct grid_cell gc;
const struct grid_cell *tmp;
struct overlay_ranges r;
u_int cell_type, x = ctx->ox + i, y = ctx->oy + j;
u_int cell_type;
u_int x = ctx->ox + i, y = ctx->oy + j;
int isolates;
struct visible_ranges *r;
if (c->overlay_check != NULL) {
c->overlay_check(c, c->overlay_data, x, y, 1, &r);
if (r.nx[0] + r.nx[1] == 0)
r = c->overlay_check(c, c->overlay_data, x, y, 1);
if (server_client_ranges_is_empty(r))
return;
}
@@ -943,7 +944,9 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
struct screen *s = wp->screen;
struct colour_palette *palette = &wp->palette;
struct grid_cell defaults;
u_int i, j, top, x, y, width;
struct visible_ranges *r;
struct visible_range *rr;
u_int i, j, k, top, x, y, width;
if (wp->base.mode & MODE_SYNC)
screen_write_stop_sync(wp);
@@ -988,7 +991,15 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
__func__, c->name, wp->id, i, j, x, y, width);
tty_default_colours(&defaults, wp);
tty_draw_line(tty, s, i, j, width, x, y, &defaults, palette);
r = tty_check_overlay_range(tty, x, y, width);
for (k = 0; k < r->used; k++) {
rr = &r->ranges[k];
if (rr->nx != 0) {
tty_draw_line(tty, s, rr->px - wp->xoff, j,
rr->nx, rr->px, y, &defaults, palette);
}
}
}
#ifdef ENABLE_SIXEL

View File

@@ -581,12 +581,12 @@ screen_check_selection(struct screen *s, u_int px, u_int py)
}
/* Get selected grid cell. */
void
int
screen_select_cell(struct screen *s, struct grid_cell *dst,
const struct grid_cell *src)
{
if (s->sel == NULL || s->sel->hidden)
return;
return (0);
memcpy(dst, &s->sel->cell, sizeof *dst);
if (COLOUR_DEFAULT(dst->fg))
@@ -600,6 +600,7 @@ screen_select_cell(struct screen *s, struct grid_cell *dst,
dst->attr |= (src->attr & GRID_ATTR_CHARSET);
else
dst->attr |= src->attr;
return (1);
}
/* Reflow wrapped lines. */

View File

@@ -161,38 +161,58 @@ server_client_clear_overlay(struct client *c)
server_redraw_client(c);
}
/* Are these ranges empty? That is, nothing is visible. */
int
server_client_ranges_is_empty(struct visible_ranges *r)
{
u_int i;
for (i = 0; i < r->used; i++) {
if (r->ranges[i].nx != 0)
return (0);
}
return (1);
}
/* Ensure we have space for at least n ranges. */
void
server_client_ensure_ranges(struct visible_ranges *r, u_int n)
{
if (r->size >= n)
return;
r->ranges = xrecallocarray(r->ranges, r->size, n, sizeof *r->ranges);
r->size = n;
}
/*
* Given overlay position and dimensions, return parts of the input range which
* are visible.
*/
void
server_client_overlay_range(u_int x, u_int y, u_int sx, u_int sy, u_int px,
u_int py, u_int nx, struct overlay_ranges *r)
u_int py, u_int nx, struct visible_ranges *r)
{
u_int ox, onx;
/* Return up to 2 ranges. */
r->px[2] = 0;
r->nx[2] = 0;
/* Trivial case of no overlap in the y direction. */
if (py < y || py > y + sy - 1) {
r->px[0] = px;
r->nx[0] = nx;
r->px[1] = 0;
r->nx[1] = 0;
server_client_ensure_ranges(r, 1);
r->ranges[0].px = px;
r->ranges[0].nx = nx;
r->used = 1;
return;
}
server_client_ensure_ranges(r, 2);
/* Visible bit to the left of the popup. */
if (px < x) {
r->px[0] = px;
r->nx[0] = x - px;
if (r->nx[0] > nx)
r->nx[0] = nx;
r->ranges[0].px = px;
r->ranges[0].nx = x - px;
if (r->ranges[0].nx > nx)
r->ranges[0].nx = nx;
} else {
r->px[0] = 0;
r->nx[0] = 0;
r->ranges[0].px = 0;
r->ranges[0].nx = 0;
}
/* Visible bit to the right of the popup. */
@@ -201,12 +221,13 @@ server_client_overlay_range(u_int x, u_int y, u_int sx, u_int sy, u_int px,
ox = px;
onx = px + nx;
if (onx > ox) {
r->px[1] = ox;
r->nx[1] = onx - ox;
r->ranges[1].px = ox;
r->ranges[1].nx = onx - ox;
} else {
r->px[1] = 0;
r->nx[1] = 0;
r->ranges[1].px = 0;
r->ranges[1].nx = 0;
}
r->used = 2;
}
/* Check if this client is inside this server. */

51
tmux.h
View File

@@ -956,7 +956,7 @@ struct screen_sel;
struct screen_titles;
struct screen {
char *title;
char *path;
char *path;
struct screen_titles *titles;
struct grid *grid; /* grid data */
@@ -1532,6 +1532,19 @@ struct key_event {
size_t len;
};
/* Visible range array element. */
struct visible_range {
u_int px; /* start */
u_int nx; /* length */
};
/* Visible areas not obstructed. */
struct visible_ranges {
struct visible_range *ranges; /* dynamically allocated array */
u_int used; /* number of entries in ranges */
u_int size; /* allocated capacity of ranges */
};
/* Terminal definition. */
struct tty_term {
char *name;
@@ -1599,6 +1612,7 @@ struct tty {
size_t discarded;
struct termios tio;
struct visible_ranges r;
struct grid_cell cell;
struct grid_cell last_cell;
@@ -1915,22 +1929,15 @@ struct client_window {
};
RB_HEAD(client_windows, client_window);
/* Visible areas not obstructed by overlays. */
#define OVERLAY_MAX_RANGES 3
struct overlay_ranges {
u_int px[OVERLAY_MAX_RANGES];
u_int nx[OVERLAY_MAX_RANGES];
};
/* Client connection. */
typedef int (*prompt_input_cb)(struct client *, void *, const char *, int);
typedef void (*prompt_free_cb)(void *);
typedef void (*overlay_check_cb)(struct client*, void *, u_int, u_int, u_int,
struct overlay_ranges *);
typedef struct visible_ranges *(*overlay_check_cb)(struct client*, void *,
u_int, u_int, u_int);
typedef struct screen *(*overlay_mode_cb)(struct client *, void *, u_int *,
u_int *);
u_int *);
typedef void (*overlay_draw_cb)(struct client *, void *,
struct screen_redraw_ctx *);
struct screen_redraw_ctx *);
typedef int (*overlay_key_cb)(struct client *, void *, struct key_event *);
typedef void (*overlay_free_cb)(struct client *, void *);
typedef void (*overlay_resize_cb)(struct client *, void *);
@@ -2522,6 +2529,8 @@ void tty_reset(struct tty *);
void tty_region_off(struct tty *);
void tty_margin_off(struct tty *);
void tty_cursor(struct tty *, u_int, u_int);
int tty_fake_bce(const struct tty *, const struct grid_cell *, u_int);
void tty_repeat_space(struct tty *, u_int);
void tty_clipboard_query(struct tty *);
void tty_putcode(struct tty *, enum tty_code_code);
void tty_putcode_i(struct tty *, enum tty_code_code, int);
@@ -2546,7 +2555,15 @@ void tty_repeat_requests(struct tty *, int);
void tty_stop_tty(struct tty *);
void tty_set_title(struct tty *, const char *);
void tty_set_path(struct tty *, const char *);
void tty_default_attributes(struct tty *, const struct grid_cell *,
struct colour_palette *, u_int, struct hyperlinks *);
void tty_update_mode(struct tty *, int, struct screen *);
const struct grid_cell *tty_check_codeset(struct tty *,
const struct grid_cell *);
struct visible_ranges *tty_check_overlay_range(struct tty *, u_int, u_int,
u_int);
/* tty-draw.c */
void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int,
u_int, u_int, const struct grid_cell *, struct colour_palette *);
@@ -2903,8 +2920,10 @@ void server_client_set_overlay(struct client *, u_int, overlay_check_cb,
overlay_mode_cb, overlay_draw_cb, overlay_key_cb,
overlay_free_cb, overlay_resize_cb, void *);
void server_client_clear_overlay(struct client *);
void server_client_ensure_ranges(struct visible_ranges *, u_int);
int server_client_ranges_is_empty(struct visible_ranges *);
void server_client_overlay_range(u_int, u_int, u_int, u_int, u_int, u_int,
u_int, struct overlay_ranges *);
u_int, struct visible_ranges *);
void server_client_set_key_table(struct client *, const char *);
const char *server_client_get_key_table(struct client *);
int server_client_check_nested(struct client *);
@@ -3220,7 +3239,7 @@ void screen_set_selection(struct screen *, u_int, u_int, u_int, u_int,
void screen_clear_selection(struct screen *);
void screen_hide_selection(struct screen *);
int screen_check_selection(struct screen *, u_int, u_int);
void screen_select_cell(struct screen *, struct grid_cell *,
int screen_select_cell(struct screen *, struct grid_cell *,
const struct grid_cell *);
void screen_alternate_on(struct screen *, struct grid_cell *, int);
void screen_alternate_off(struct screen *, struct grid_cell *, int);
@@ -3607,8 +3626,8 @@ int menu_display(struct menu *, int, int, struct cmdq_item *,
const char *, const char *, struct cmd_find_state *,
menu_choice_cb, void *);
struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *);
void menu_check_cb(struct client *, void *, u_int, u_int, u_int,
struct overlay_ranges *);
struct visible_ranges *menu_check_cb(struct client *, void *, u_int, u_int,
u_int);
void menu_draw_cb(struct client *, void *,
struct screen_redraw_ctx *);
void menu_free_cb(struct client *, void *);

319
tty-draw.c Normal file
View File

@@ -0,0 +1,319 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <string.h>
#include "tmux.h"
enum tty_draw_line_state {
TTY_DRAW_LINE_FIRST,
TTY_DRAW_LINE_FLUSH,
TTY_DRAW_LINE_NEW1,
TTY_DRAW_LINE_NEW2,
TTY_DRAW_LINE_EMPTY,
TTY_DRAW_LINE_SAME,
TTY_DRAW_LINE_PAD,
TTY_DRAW_LINE_DONE
};
static const char* tty_draw_line_states[] = {
"FIRST",
"FLUSH",
"NEW1",
"NEW2",
"EMPTY",
"SAME",
"PAD",
"DONE"
};
/* Clear part of the line. */
static void
tty_draw_line_clear(struct tty *tty, u_int px, u_int py, u_int nx,
const struct grid_cell *defaults, u_int bg, int wrapped)
{
/* Nothing to clear. */
if (nx == 0)
return;
/* If genuine BCE is available, can try escape sequences. */
if (!wrapped && nx >= 10 && !tty_fake_bce(tty, defaults, bg)) {
/* Off the end of the line, use EL if available. */
if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) {
tty_cursor(tty, px, py);
tty_putcode(tty, TTYC_EL);
return;
}
/* At the start of the line. Use EL1. */
if (px == 0 && tty_term_has(tty->term, TTYC_EL1)) {
tty_cursor(tty, px + nx - 1, py);
tty_putcode(tty, TTYC_EL1);
return;
}
/* Section of line. Use ECH if possible. */
if (tty_term_has(tty->term, TTYC_ECH)) {
tty_cursor(tty, px, py);
tty_putcode_i(tty, TTYC_ECH, nx);
return;
}
}
/* Couldn't use an escape sequence, use spaces. */
if (px != 0 || !wrapped)
tty_cursor(tty, px, py);
if (nx == 1)
tty_putc(tty, ' ');
else if (nx == 2)
tty_putn(tty, " ", 2, 2);
else
tty_repeat_space(tty, nx);
}
/* Is this cell empty? */
static u_int
tty_draw_line_get_empty(const struct grid_cell *gc, u_int nx)
{
u_int empty = 0;
if (gc->data.width != 1 && gc->data.width > nx)
empty = nx;
else if (gc->attr == 0 && gc->link == 0) {
if (gc->flags & GRID_FLAG_CLEARED)
empty = 1;
else if (gc->flags & GRID_FLAG_TAB)
empty = gc->data.width;
else if (gc->data.size == 1 && *gc->data.data == ' ')
empty = 1;
}
return (empty);
}
/* Draw a line from screen to tty. */
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;
const struct grid_cell *gcp;
struct grid_cell gc, ngc, last;
struct grid_line *gl;
u_int i, j, last_i, cx, ex, width;
u_int cellsize, bg;
int flags, empty, wrapped = 0;
char buf[1000];
size_t len;
enum tty_draw_line_state current_state, next_state;
/*
* 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.
*/
log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__, px, py, nx,
atx, aty);
/*
* Clamp the width to cellsize - note this is not cellused, because
* there may be empty background cells after it (from BCE).
*/
cellsize = grid_get_line(gd, gd->hsize + py)->cellsize;
if (screen_size_x(s) > cellsize)
ex = cellsize;
else {
ex = screen_size_x(s);
if (px > ex)
return;
if (px + nx > ex)
nx = ex - px;
}
if (ex < nx)
ex = nx;
log_debug("%s: drawing %u-%u,%u (end %u) at %u,%u; defaults: fg=%d, "
"bg=%d", __func__, px, px + nx, py, ex, atx, aty, defaults->fg,
defaults->bg);
/*
* If there is padding at the start, we must have truncated a wide
* character. Clear it.
*/
cx = 0;
for (i = px; i < px + nx; i++) {
grid_view_get_cell(gd, i, py, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
cx++;
}
if (cx != 0) {
/* Find the previous cell for the background colour. */
for (i = px + 1; i > 0; i--) {
grid_view_get_cell(gd, i - 1, py, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
}
if (i == 0)
bg = defaults->bg;
else {
bg = gc.bg;
if (gc.flags & GRID_FLAG_SELECTED) {
memcpy(&ngc, &gc, sizeof ngc);
if (screen_select_cell(s, &ngc, &gc))
bg = ngc.bg;
}
}
tty_attributes(tty, &last, defaults, palette, s->hyperlinks);
log_debug("%s: clearing %u padding cells", __func__, cx);
tty_draw_line_clear(tty, atx, aty, cx, defaults, bg, 0);
if (cx == ex)
return;
atx += cx;
px += cx;
nx -= cx;
ex -= cx;
}
/* Did the previous line wrap on to this one? */
if (py != 0 && atx == 0 && tty->cx >= tty->sx && nx == tty->sx) {
gl = grid_get_line(gd, gd->hsize + py - 1);
if (gl->flags & GRID_LINE_WRAPPED)
wrapped = 1;
}
/* Turn off cursor while redrawing and reset region and margins. */
flags = (tty->flags & TTY_NOCURSOR);
tty->flags |= TTY_NOCURSOR;
tty_update_mode(tty, tty->mode, s);
tty_region_off(tty);
tty_margin_off(tty);
/* Start with the default cell as the last cell. */
memcpy(&last, &grid_default_cell, sizeof last);
last.bg = defaults->bg;
tty_default_attributes(tty, defaults, palette, 8, s->hyperlinks);
/* Loop over each character in the range. */
last_i = i = 0;
len = 0;
width = 0;
current_state = TTY_DRAW_LINE_FIRST;
for (;;) {
/* Work out the next state. */
if (i == nx) {
/*
* If this is the last cell, we are done. But we need to
* go through the loop again to flush anything in
* the buffer.
*/
empty = 0;
next_state = TTY_DRAW_LINE_DONE;
} else {
/* Get the current cell. */
grid_view_get_cell(gd, px + i, py, &gc);
/* Update for codeset if needed. */
gcp = tty_check_codeset(tty, &gc);
/* And for selection. */
if (gcp->flags & GRID_FLAG_SELECTED) {
memcpy(&ngc, gcp, sizeof ngc);
if (screen_select_cell(s, &ngc, gcp))
gcp = &ngc;
}
/* Work out the the empty width. */
if (i >= ex)
empty = 1;
else
empty = tty_draw_line_get_empty(gcp, nx - i);
/* Work out the next state. */
if (empty != 0)
next_state = TTY_DRAW_LINE_EMPTY;
else if (current_state == TTY_DRAW_LINE_FIRST)
next_state = TTY_DRAW_LINE_SAME;
else if (gcp->flags & GRID_FLAG_PADDING)
next_state = TTY_DRAW_LINE_PAD;
else if (grid_cells_look_equal(gcp, &last)) {
if (gcp->data.size > (sizeof buf) - len)
next_state = TTY_DRAW_LINE_FLUSH;
else
next_state = TTY_DRAW_LINE_SAME;
} else if (current_state == TTY_DRAW_LINE_NEW1)
next_state = TTY_DRAW_LINE_NEW2;
else
next_state = TTY_DRAW_LINE_NEW1;
}
log_debug("%s: cell %u empty %u, bg %u; state: current %s, "
"next %s", __func__, px + i, empty, gcp->bg,
tty_draw_line_states[current_state],
tty_draw_line_states[next_state]);
/* If the state has changed, flush any collected data. */
if (next_state != current_state) {
if (current_state == TTY_DRAW_LINE_EMPTY) {
tty_attributes(tty, &last, defaults, palette,
s->hyperlinks);
tty_draw_line_clear(tty, atx + last_i, aty,
i - last_i, defaults, last.bg, wrapped);
wrapped = 0;
} else if (next_state != TTY_DRAW_LINE_SAME &&
len != 0) {
tty_attributes(tty, &last, defaults, palette,
s->hyperlinks);
if (atx + i - width != 0 || !wrapped)
tty_cursor(tty, atx + i - width, aty);
if (~last.attr & GRID_ATTR_CHARSET)
tty_putn(tty, buf, len, width);
else {
for (j = 0; j < len; j++)
tty_putc(tty, buf[j]);
}
len = 0;
width = 0;
wrapped = 0;
}
last_i = i;
}
/* Append the cell if it is not empty and not padding. */
if (next_state != TTY_DRAW_LINE_EMPTY &&
next_state != TTY_DRAW_LINE_PAD) {
memcpy(buf + len, gcp->data.data, gcp->data.size);
len += gcp->data.size;
width += gcp->data.width;
}
/* If this is the last cell, we are done. */
if (next_state == TTY_DRAW_LINE_DONE)
break;
/* Otherwise move to the next. */
current_state = next_state;
memcpy(&last, gcp, sizeof last);
if (empty != 0)
i += empty;
else
i += gcp->data.width;
}
tty->flags = (tty->flags & ~TTY_NOCURSOR)|flags;
tty_update_mode(tty, tty->mode, s);
}

281
tty.c
View File

@@ -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 *);
#ifdef ENABLE_SIXEL
static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
@@ -528,6 +521,8 @@ void
tty_free(struct tty *tty)
{
tty_close(tty);
free(tty->r.ranges);
}
void
@@ -912,7 +907,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];
@@ -1071,7 +1066,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))
@@ -1166,8 +1161,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);
@@ -1203,13 +1196,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. */
@@ -1390,7 +1378,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;
@@ -1423,229 +1411,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));
}
#ifdef ENABLE_SIXEL
@@ -2173,7 +1966,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;
@@ -2190,9 +1983,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);
@@ -2218,7 +2011,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;
@@ -2243,20 +2037,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);
}
}
}
@@ -3203,7 +2998,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)
{