diff --git a/format-draw.c b/format-draw.c index bd32b2a8..ec98ba95 100644 --- a/format-draw.c +++ b/format-draw.c @@ -600,7 +600,8 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, /* If this style pushed or popped the default, update it. */ if (sy.default_type == STYLE_DEFAULT_PUSH) { - memcpy(¤t_default, &saved_sy.gc, sizeof current_default); + memcpy(¤t_default, &saved_sy.gc, + sizeof current_default); sy.default_type = STYLE_DEFAULT_BASE; } else if (sy.default_type == STYLE_DEFAULT_POP) { memcpy(¤t_default, base, sizeof current_default); diff --git a/options-table.c b/options-table.c index b1c4ec1b..4ac0d0c3 100644 --- a/options-table.c +++ b/options-table.c @@ -60,6 +60,9 @@ static const char *options_table_visual_bell_list[] = { static const char *options_table_pane_status_list[] = { "off", "top", "bottom", NULL }; +static const char *options_table_pane_lines_list[] = { + "single", "double", "heavy", "simple", "number", NULL +}; static const char *options_table_set_clipboard_list[] = { "off", "external", "on", NULL }; @@ -904,6 +907,14 @@ const struct options_table_entry options_table[] = { .text = "Format of text in the pane status lines." }, + { .name = "pane-border-lines", + .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_WINDOW, + .choices = options_table_pane_lines_list, + .default_num = PANE_LINES_SINGLE, + .text = "Type of the pane type lines." + }, + { .name = "pane-border-status", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, diff --git a/screen-redraw.c b/screen-redraw.c index ffa7aecf..002970e9 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -45,8 +45,36 @@ static void screen_redraw_draw_pane(struct screen_redraw_ctx *, #define CELL_BORDERS " xqlkmjwvtun~" -const struct grid_cell screen_redraw_border_cell = { - { { ' ' }, 0, 1, 1 }, GRID_ATTR_CHARSET, 0, 8, 8, 0 +static const struct utf8_data screen_redraw_double_borders[] = { + { "", 0, 0, 0 }, + { "\342\225\221", 0, 3, 1 }, /* U+2551 */ + { "\342\225\220", 0, 3, 1 }, /* U+2550 */ + { "\342\225\224", 0, 3, 1 }, /* U+2554 */ + { "\342\225\227", 0, 3, 1 }, /* U+2557 */ + { "\342\225\232", 0, 3, 1 }, /* U+255A */ + { "\342\225\235", 0, 3, 1 }, /* U+255D */ + { "\342\225\246", 0, 3, 1 }, /* U+2566 */ + { "\342\225\251", 0, 3, 1 }, /* U+2569 */ + { "\342\225\240", 0, 3, 1 }, /* U+2560 */ + { "\342\225\243", 0, 3, 1 }, /* U+2563 */ + { "\342\225\254", 0, 3, 1 }, /* U+256C */ + { "\302\267", 0, 2, 1 } /* U+00B7 */ +}; + +static const struct utf8_data screen_redraw_heavy_borders[] = { + { "", 0, 0, 0 }, + { "\342\224\203", 0, 3, 1 }, /* U+2503 */ + { "\342\224\201", 0, 3, 1 }, /* U+2501 */ + { "\342\224\223", 0, 3, 1 }, /* U+2513 */ + { "\342\224\217", 0, 3, 1 }, /* U+250F */ + { "\342\224\227", 0, 3, 1 }, /* U+2517 */ + { "\342\224\233", 0, 3, 1 }, /* U+251B */ + { "\342\224\263", 0, 3, 1 }, /* U+2533 */ + { "\342\224\273", 0, 3, 1 }, /* U+253B */ + { "\342\224\243", 0, 3, 1 }, /* U+2523 */ + { "\342\224\253", 0, 3, 1 }, /* U+252B */ + { "\342\225\213", 0, 3, 1 }, /* U+254B */ + { "\302\267", 0, 2, 1 } /* U+00B7 */ }; enum screen_redraw_border_type { @@ -55,6 +83,45 @@ enum screen_redraw_border_type { SCREEN_REDRAW_BORDER }; +/* Get cell border character. */ +static void +screen_redraw_border_set(struct window_pane *wp, int pane_lines, int cell_type, + struct grid_cell *gc) +{ + u_int idx; + + switch (pane_lines) { + case PANE_LINES_NUMBER: + if (cell_type == CELL_OUTSIDE) { + gc->attr |= GRID_ATTR_CHARSET; + utf8_set(&gc->data, CELL_BORDERS[CELL_OUTSIDE]); + break; + } + gc->attr &= ~GRID_ATTR_CHARSET; + if (wp != NULL && window_pane_index(wp, &idx) == 0) + utf8_set(&gc->data, '0' + (idx % 10)); + else + utf8_set(&gc->data, '*'); + break; + case PANE_LINES_DOUBLE: + gc->attr &= ~GRID_ATTR_CHARSET; + utf8_copy(&gc->data, &screen_redraw_double_borders[cell_type]); + break; + case PANE_LINES_HEAVY: + gc->attr &= ~GRID_ATTR_CHARSET; + utf8_copy(&gc->data, &screen_redraw_heavy_borders[cell_type]); + break; + case PANE_LINES_SIMPLE: + gc->attr &= ~GRID_ATTR_CHARSET; + utf8_set(&gc->data, " |-+++++++++."[cell_type]); + break; + default: + gc->attr |= GRID_ATTR_CHARSET; + utf8_set(&gc->data, CELL_BORDERS[cell_type]); + break; + } +} + /* Return if window has only two panes. */ static int screen_redraw_two_panes(struct window *w, int direction) @@ -317,7 +384,7 @@ screen_redraw_check_is(u_int px, u_int py, int pane_status, /* Update pane status. */ static int screen_redraw_make_pane_status(struct client *c, struct window *w, - struct window_pane *wp) + struct window_pane *wp, int pane_lines) { struct grid_cell gc; const char *fmt; @@ -348,9 +415,9 @@ screen_redraw_make_pane_status(struct client *c, struct window *w, screen_write_start(&ctx, &wp->status_screen); - gc.attr |= GRID_ATTR_CHARSET; + screen_redraw_border_set(wp, pane_lines, CELL_TOPBOTTOM, &gc); for (i = 0; i < width; i++) - screen_write_putc(&ctx, &gc, 'q'); + screen_write_cell(&ctx, &gc); gc.attr &= ~GRID_ATTR_CHARSET; screen_write_cursormove(&ctx, 0, 0, 0); @@ -436,7 +503,7 @@ screen_redraw_update(struct client *c, int flags) struct window *w = c->session->curw->window; struct window_pane *wp; struct options *wo = w->options; - int redraw; + int redraw, lines; if (c->message_string != NULL) redraw = status_message_redraw(c); @@ -451,9 +518,10 @@ screen_redraw_update(struct client *c, int flags) flags |= CLIENT_REDRAWOVERLAY; if (options_get_number(wo, "pane-border-status") != PANE_STATUS_OFF) { + lines = options_get_number(wo, "pane-border-lines"); redraw = 0; TAILQ_FOREACH(wp, &w->panes, entry) { - if (screen_redraw_make_pane_status(c, w, wp)) + if (screen_redraw_make_pane_status(c, w, wp, lines)) redraw = 1; } if (redraw) @@ -483,6 +551,7 @@ screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx) ctx->statuslines = lines; ctx->pane_status = options_get_number(wo, "pane-border-status"); + ctx->pane_lines = options_get_number(wo, "pane-border-lines"); tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy); @@ -560,7 +629,6 @@ screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, struct window *w = s->curw->window; struct window_pane *active = server_client_get_pane(c); struct options *oo = w->options; - struct grid_cell *gc; struct format_tree *ft; if (wp->border_gc_set) @@ -568,18 +636,13 @@ screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, wp->border_gc_set = 1; ft = format_create_defaults(NULL, c, s, s->curw, wp); - gc = &wp->border_gc; - - if (screen_redraw_check_is(x, y, ctx->pane_status, active)) { - style_apply(gc, oo, "pane-active-border-style", ft); - gc->attr |= GRID_ATTR_CHARSET; - } else { - style_apply(gc, oo, "pane-border-style", ft); - gc->attr |= GRID_ATTR_CHARSET; - } - + if (screen_redraw_check_is(x, y, ctx->pane_status, active)) + style_apply(&wp->border_gc, oo, "pane-active-border-style", ft); + else + style_apply(&wp->border_gc, oo, "pane-border-style", ft); format_free(ft); - return (gc); + + return (&wp->border_gc); } /* Draw a border cell. */ @@ -590,39 +653,37 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) struct session *s = c->session; struct tty *tty = &c->tty; struct window_pane *wp; - u_int type, x = ctx->ox + i, y = ctx->oy + j; + u_int cell_type, x = ctx->ox + i, y = ctx->oy + j; int pane_status = ctx->pane_status; - const struct grid_cell *gc; - struct grid_cell copy; + struct grid_cell gc; + const struct grid_cell *tmp; if (c->overlay_check != NULL && !c->overlay_check(c, x, y)) return; - type = screen_redraw_check_cell(c, x, y, pane_status, &wp); - if (type == CELL_INSIDE) + cell_type = screen_redraw_check_cell(c, x, y, pane_status, &wp); + if (cell_type == CELL_INSIDE) return; if (wp == NULL) - gc = &screen_redraw_border_cell; + memcpy(&gc, &grid_default_cell, sizeof gc); else { - gc = screen_redraw_draw_borders_style(ctx, x, y, wp); - if (gc == NULL) + tmp = screen_redraw_draw_borders_style(ctx, x, y, wp); + if (tmp == NULL) return; + memcpy(&gc, tmp, sizeof gc); if (server_is_marked(s, s->curw, marked_pane.wp) && - screen_redraw_check_is(x, y, pane_status, marked_pane.wp)) { - memcpy(©, gc, sizeof copy); - copy.attr ^= GRID_ATTR_REVERSE; - gc = © - } + screen_redraw_check_is(x, y, pane_status, marked_pane.wp)) + gc.attr ^= GRID_ATTR_REVERSE; } + screen_redraw_border_set(wp, ctx->pane_lines, cell_type, &gc); - tty_attributes(tty, gc, &grid_default_cell, NULL); if (ctx->statustop) tty_cursor(tty, i, ctx->statuslines + j); else tty_cursor(tty, i, j); - tty_putc(tty, CELL_BORDERS[type]); + tty_cell(tty, &gc, &grid_default_cell, NULL); } /* Draw the borders. */ diff --git a/tmux.1 b/tmux.1 index 0b03becc..421f8889 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3924,6 +3924,28 @@ but set the starting index for pane numbers. .It Ic pane-border-format Ar format Set the text shown in pane border status lines. .Pp +.It Ic pane-border-lines Ar type +Set the type of characters used for drawing pane borders. +.Ar type +may be one of: +.Bl -tag -width Ds +.It single +single lines using ACS or UTF-8 characters +.It double +double lines using UTF-8 characters +.It heavy +heavy lines using UTF-8 characters +.It simple +simple ASCII characters +.It number +the pane number +.El +.Pp +.Ql double +and +.Ql heavy +will fall back to standard ACS line drawing when UTF-8 is not supported. +.Pp .It Xo Ic pane-border-status .Op Ic off | top | bottom .Xc diff --git a/tmux.h b/tmux.h index 921a51f9..a413e0cf 100644 --- a/tmux.h +++ b/tmux.h @@ -820,6 +820,7 @@ struct screen_redraw_ctx { int statustop; int pane_status; + int pane_lines; u_int sx; u_int sy; @@ -1050,6 +1051,13 @@ TAILQ_HEAD(winlink_stack, winlink); #define PANE_STATUS_TOP 1 #define PANE_STATUS_BOTTOM 2 +/* Pane border lines option. */ +#define PANE_LINES_SINGLE 0 +#define PANE_LINES_DOUBLE 1 +#define PANE_LINES_HEAVY 2 +#define PANE_LINES_SIMPLE 3 +#define PANE_LINES_NUMBER 4 + /* Layout direction. */ enum layout_type { LAYOUT_LEFTRIGHT, @@ -2030,6 +2038,8 @@ void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *, void tty_puts(struct tty *, const char *); void tty_putc(struct tty *, u_char); void tty_putn(struct tty *, const void *, size_t, u_int); +void tty_cell(struct tty *, const struct grid_cell *, + const struct grid_cell *, int *); int tty_init(struct tty *, struct client *, int); void tty_resize(struct tty *); void tty_set_size(struct tty *, u_int, u_int, u_int, u_int); @@ -2103,6 +2113,7 @@ void tty_default_features(int *, const char *, u_int); /* tty-acs.c */ int tty_acs_needed(struct tty *); const char *tty_acs_get(struct tty *, u_char); +int tty_acs_reverse_get(struct tty *, const char *, size_t); /* tty-keys.c */ void tty_keys_build(struct tty *); diff --git a/tty-acs.c b/tty-acs.c index 3e811103..63eccb93 100644 --- a/tty-acs.c +++ b/tty-acs.c @@ -19,11 +19,10 @@ #include #include +#include #include "tmux.h" -static int tty_acs_cmp(const void *, const void *); - /* Table mapping ACS entries to UTF-8. */ struct tty_acs_entry { u_char key; @@ -68,14 +67,65 @@ static const struct tty_acs_entry tty_acs_table[] = { { '~', "\302\267" } /* bullet */ }; +/* Table mapping UTF-8 to ACS entries. */ +struct tty_acs_reverse_entry { + const char *string; + u_char key; +}; +static const struct tty_acs_reverse_entry tty_acs_reverse2[] = { + { "\302\267", '~' } +}; +static const struct tty_acs_reverse_entry tty_acs_reverse3[] = { + { "\342\224\200", 'q' }, + { "\342\224\201", 'q' }, + { "\342\224\202", 'x' }, + { "\342\224\203", 'x' }, + { "\342\224\214", 'l' }, + { "\342\224\217", 'k' }, + { "\342\224\220", 'k' }, + { "\342\224\223", 'l' }, + { "\342\224\224", 'm' }, + { "\342\224\227", 'm' }, + { "\342\224\230", 'j' }, + { "\342\224\233", 'j' }, + { "\342\224\234", 't' }, + { "\342\224\243", 't' }, + { "\342\224\244", 'u' }, + { "\342\224\253", 'u' }, + { "\342\224\263", 'w' }, + { "\342\224\264", 'v' }, + { "\342\224\273", 'v' }, + { "\342\224\274", 'n' }, + { "\342\225\213", 'n' }, + { "\342\225\220", 'q' }, + { "\342\225\221", 'x' }, + { "\342\225\224", 'l' }, + { "\342\225\227", 'k' }, + { "\342\225\232", 'm' }, + { "\342\225\235", 'j' }, + { "\342\225\240", 't' }, + { "\342\225\243", 'u' }, + { "\342\225\246", 'w' }, + { "\342\225\251", 'v' }, + { "\342\225\254", 'n' }, +}; + static int tty_acs_cmp(const void *key, const void *value) { const struct tty_acs_entry *entry = value; - u_char ch; + int test = *(u_char *)key; - ch = *(u_char *) key; - return (ch - entry->key); + return (test - entry->key); +} + +static int +tty_acs_reverse_cmp(const void *key, const void *value) +{ + const struct tty_acs_reverse_entry *entry = value; + const char *test = key; + + return (strcmp(test, entry->string)); } /* Should this terminal use ACS instead of UTF-8 line drawing? */ @@ -104,11 +154,11 @@ tty_acs_needed(struct tty *tty) return (1); } -/* Retrieve ACS to output as a string. */ +/* Retrieve ACS to output as UTF-8. */ const char * tty_acs_get(struct tty *tty, u_char ch) { - struct tty_acs_entry *entry; + const struct tty_acs_entry *entry; /* Use the ACS set instead of UTF-8 if needed. */ if (tty_acs_needed(tty)) { @@ -124,3 +174,24 @@ tty_acs_get(struct tty *tty, u_char ch) return (NULL); return (entry->string); } + +/* Reverse UTF-8 into ACS. */ +int +tty_acs_reverse_get(__unused struct tty *tty, const char *s, size_t slen) +{ + const struct tty_acs_reverse_entry *table, *entry; + u_int items; + + if (slen == 2) { + table = tty_acs_reverse2; + items = nitems(tty_acs_reverse2); + } else if (slen == 3) { + table = tty_acs_reverse3; + items = nitems(tty_acs_reverse3); + } else + return (-1); + entry = bsearch(s, table, items, sizeof table[0], tty_acs_reverse_cmp); + if (entry == NULL) + return (-1); + return (entry->key); +} diff --git a/tty.c b/tty.c index a4f2acee..99996dfa 100644 --- a/tty.c +++ b/tty.c @@ -65,8 +65,6 @@ 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_cell(struct tty *, const struct grid_cell *, - const struct grid_cell *, int *); static void tty_default_attributes(struct tty *, const struct grid_cell *, int *, u_int); @@ -1243,7 +1241,7 @@ static const struct grid_cell * tty_check_codeset(struct tty *tty, const struct grid_cell *gc) { static struct grid_cell new; - u_int n; + int c; /* Characters less than 0x7f are always fine, no matter what. */ if (gc->data.size == 1 && *gc->data.data < 0x7f) @@ -1252,14 +1250,21 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc) /* UTF-8 terminal and a UTF-8 character - fine. */ if (tty->client->flags & CLIENT_UTF8) return (gc); + memcpy(&new, gc, sizeof new); + + /* See if this can be mapped to an ACS character. */ + c = tty_acs_reverse_get(tty, gc->data.data, gc->data.size); + if (c != -1) { + utf8_set(&new.data, c); + new.attr |= GRID_ATTR_CHARSET; + return (&new); + } /* Replace by the right number of underscores. */ - n = gc->data.width; - if (n > UTF8_SIZE) - n = UTF8_SIZE; - memcpy(&new, gc, sizeof new); - new.data.size = n; - memset(new.data.data, '_', n); + new.data.size = gc->data.width; + if (new.data.size > UTF8_SIZE) + new.data.size = UTF8_SIZE; + memset(new.data.data, '_', new.data.size); return (&new); } @@ -1924,7 +1929,7 @@ tty_cmd_syncstart(struct tty *tty, __unused const struct tty_ctx *ctx) tty_sync_start(tty); } -static void +void tty_cell(struct tty *tty, const struct grid_cell *gc, const struct grid_cell *defaults, int *palette) { @@ -1940,12 +1945,13 @@ tty_cell(struct tty *tty, const struct grid_cell *gc, if (gc->flags & GRID_FLAG_PADDING) return; - /* Set the attributes. */ - tty_attributes(tty, gc, defaults, palette); - - /* Get the cell and if ASCII write with putc to do ACS translation. */ + /* Check the output codeset and apply attributes. */ gcp = tty_check_codeset(tty, gc); + tty_attributes(tty, gcp, defaults, palette); + + /* If it is a single character, write with putc to handle ACS. */ if (gcp->data.size == 1) { + tty_attributes(tty, gcp, defaults, palette); if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f) return; tty_putc(tty, *gcp->data.data);