Merge branch 'master' into floating_panes

This commit is contained in:
Dane Jensen
2026-06-29 15:25:54 -07:00
97 changed files with 10386 additions and 4219 deletions

2
.gitignore vendored
View File

@@ -7,6 +7,8 @@
*~
.deps/
.dirstamp
.serena/
AGENTS.md
Makefile
Makefile.in
aclocal.m4

View File

@@ -163,6 +163,7 @@ dist_tmux_SOURCES = \
file.c \
format.c \
format-draw.c \
fuzzy.c \
grid-reader.c \
grid-view.c \
grid.c \
@@ -185,6 +186,8 @@ dist_tmux_SOURCES = \
paste.c \
popup.c \
proc.c \
prompt.c \
prompt-history.c \
regsub.c \
resize.c \
screen-redraw.c \
@@ -210,11 +213,13 @@ dist_tmux_SOURCES = \
tty.c \
utf8-combined.c \
utf8.c \
window-border.c \
window-buffer.c \
window-client.c \
window-clock.c \
window-copy.c \
window-customize.c \
window-switch.c \
window-tree.c \
window-visible.c \
window.c \

View File

@@ -149,7 +149,7 @@ args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
int optional_argument)
{
struct args_value *argument, *new;
const char *s;
const char *s, *as;
new = xcalloc(1, sizeof *new);
if (*string != '\0') {
@@ -180,12 +180,24 @@ args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
xasprintf(cause, "-%c expects an argument", flag);
return (-1);
}
if (optional_argument && argument->type == ARGS_STRING) {
as = argument->string;
if (as[0] == '-' && (as[1] == '-' || isalpha((u_char)as[1]))) {
args_free_value(new);
free(new);
log_debug("%s: -%c (optional)", __func__, flag);
args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE);
return (0);
}
}
args_copy_value(new, argument);
(*i)++;
out:
s = args_value_as_string(new);
log_debug("%s: -%c = %s", __func__, flag, s);
args_set(args, flag, new, 0);
return (0);
}
@@ -998,7 +1010,7 @@ args_string_percentage(const char *value, long long minval, long long maxval,
copy = xstrdup(value);
copy[valuelen - 1] = '\0';
ll = strtonum(copy, 0, 100, &errstr);
ll = strtonum(copy, 0, 1000, &errstr);
free(copy);
if (errstr != NULL) {
*cause = xstrdup(errstr);
@@ -1066,7 +1078,7 @@ args_string_percentage_and_expand(const char *value, long long minval,
copy[valuelen - 1] = '\0';
f = format_single_from_target(item, copy);
ll = strtonum(f, 0, 100, &errstr);
ll = strtonum(f, 0, 1000, &errstr);
free(f);
free(copy);
if (errstr != NULL) {

2
cfg.c
View File

@@ -56,7 +56,7 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
if (cfg_item != NULL)
cmdq_continue(cfg_item);
status_prompt_load_history();
prompt_load_history();
return (CMD_RETURN_NORMAL);
}

View File

@@ -54,6 +54,7 @@ cmd_break_pane_float(struct cmdq_item *item, struct args *args,
u_int sx = lc->saved_sx, sy = lc->saved_sy;
int ox = lc->saved_xoff, oy = lc->saved_yoff;
char *cause = NULL;
enum pane_lines lines = window_get_pane_lines(w);
if (window_pane_is_floating(wp)) {
cmdq_error(item, "pane is already floating");
@@ -68,13 +69,13 @@ cmd_break_pane_float(struct cmdq_item *item, struct args *args,
return (CMD_RETURN_ERROR);
}
layout_remove_tile(w, lc);
layout_cell_floating_args_parse(item, args, w, &sx, &sy, &ox, &oy, &cause);
if (cause != NULL) {
if (layout_floating_args_parse(item, args, lines, w, &sx, &sy, &ox, &oy,
&cause) != 0) {
cmdq_error(item, "failed to float pane: %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
layout_remove_tile(w, lc);
layout_set_size(lc, sx, sy, ox, oy);
lc->flags |= LAYOUT_CELL_FLOATING;
@@ -111,7 +112,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'W'))
return (cmd_break_pane_float(item, args, w, wp));
if (name != NULL && !check_name(name, WINDOW_NAME_FORBID)) {
if (name != NULL && !check_name(name)) {
cmdq_error(item, "invalid window name: %s", name);
return (CMD_RETURN_ERROR);
}
@@ -135,7 +136,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
if (name != NULL) {
window_set_name(w, name, WINDOW_NAME_FORBID);
window_set_name(w, name, 0);
options_set_number(w->options, "automatic-rename", 0);
}
server_unlink_window(src_s, wl);
@@ -165,7 +166,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
if (name == NULL) {
newname = default_window_name(w);
window_set_name(w, newname, WINDOW_NAME_FORBID);
window_set_name(w, newname, 0);
free(newname);
} else {
window_set_name(w, name, 0);

View File

@@ -318,6 +318,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
grid_clear_history(wp->base.grid);
if (args_has(args, 'H'))
screen_reset_hyperlinks(wp->screen);
server_redraw_window(wp->window);
return (CMD_RETURN_NORMAL);
}

View File

@@ -84,6 +84,19 @@ const struct cmd_entry cmd_customize_mode_entry = {
.exec = cmd_choose_tree_exec
};
const struct cmd_entry cmd_switch_mode_entry = {
.name = "switch-mode",
.alias = NULL,
.args = { "F:kst:wZ", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-kswZ] [-F format] " CMD_TARGET_PANE_USAGE " [command]",
.target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.exec = cmd_choose_tree_exec
};
static enum args_parse_type
cmd_choose_tree_args_parse(__unused struct args *args, __unused u_int idx,
__unused char **cause)
@@ -116,6 +129,8 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
mode = &window_client_mode;
} else if (cmd_get_entry(self) == &cmd_customize_mode_entry)
mode = &window_customize_mode;
else if (cmd_get_entry(self) == &cmd_switch_mode_entry)
mode = &window_switch_mode;
else
mode = &window_tree_mode;

View File

@@ -34,16 +34,16 @@ static enum args_parse_type cmd_command_prompt_args_parse(struct args *,
static enum cmd_retval cmd_command_prompt_exec(struct cmd *,
struct cmdq_item *);
static int cmd_command_prompt_callback(struct client *, void *,
const char *, int);
static enum prompt_result cmd_command_prompt_callback(struct client *, void *,
const char *, enum prompt_key_result);
static void cmd_command_prompt_free(void *);
const struct cmd_entry cmd_command_prompt_entry = {
.name = "command-prompt",
.alias = NULL,
.args = { "1CbeFiklI:Np:t:T:", 0, 1, cmd_command_prompt_args_parse },
.usage = "[-1CbeFiklN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
.args = { "1CbeFiklI:NPp:t:T:", 0, 1, cmd_command_prompt_args_parse },
.usage = "[-1CbeFiklNP] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
" [-T prompt-type] [template]",
.flags = CMD_CLIENT_TFLAG,
@@ -62,6 +62,8 @@ struct cmd_command_prompt_cdata {
int flags;
enum prompt_type prompt_type;
struct window_pane *wp;
struct cmd_command_prompt_prompt *prompts;
u_int count;
u_int current;
@@ -87,10 +89,15 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_command_prompt_cdata *cdata;
char *tmp, *prompts, *prompt, *next_prompt;
char *inputs = NULL, *next_input;
struct window_pane *wp = target->wp;
u_int count = args_count(args);
int wait = !args_has(args, 'b'), space = 1;
int pane = args_has(args, 'P');
if (tc->prompt_string != NULL)
if (pane) {
if (wp == NULL || window_pane_has_prompt(wp))
return (CMD_RETURN_NORMAL);
} else if (tc->prompt != NULL)
return (CMD_RETURN_NORMAL);
if (args_has(args, 'i'))
wait = 0;
@@ -98,6 +105,8 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
cdata = xcalloc(1, sizeof *cdata);
if (wait)
cdata->item = item;
if (pane)
cdata->wp = wp;
cdata->state = args_make_commands_prepare(self, item, 0, "%1", wait,
args_has(args, 'F'));
@@ -146,7 +155,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
}
if ((type = args_get(args, 'T')) != NULL) {
cdata->prompt_type = status_prompt_type(type);
cdata->prompt_type = prompt_type(type);
if (cdata->prompt_type == PROMPT_TYPE_INVALID) {
cmdq_error(item, "unknown type: %s", type);
cmd_command_prompt_free(cdata);
@@ -167,18 +176,27 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
cdata->flags |= PROMPT_BSPACE_EXIT;
if (args_has(args, 'C'))
cdata->flags |= PROMPT_NOFREEZE;
status_prompt_set(tc, target, cdata->prompts[0].prompt,
cdata->prompts[0].input, cmd_command_prompt_callback,
cmd_command_prompt_free, cdata, cdata->flags, cdata->prompt_type);
if (pane) {
cdata->flags |= PROMPT_ISPANE;
window_pane_set_prompt(wp, tc, target, cdata->prompts[0].prompt,
cdata->prompts[0].input, cmd_command_prompt_callback,
cmd_command_prompt_free, cdata, cdata->flags,
cdata->prompt_type);
} else {
status_prompt_set(tc, target, cdata->prompts[0].prompt,
cdata->prompts[0].input, cmd_command_prompt_callback,
cmd_command_prompt_free, cdata, cdata->flags,
cdata->prompt_type);
}
if (!wait)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}
static int
static enum prompt_result
cmd_command_prompt_callback(struct client *c, void *data, const char *s,
int done)
enum prompt_key_result key)
{
struct cmd_command_prompt_cdata *cdata = data;
char *error;
@@ -188,26 +206,30 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
int argc = 0;
char **argv = NULL;
if (s == NULL)
if (s == NULL || key == PROMPT_KEY_MOVE)
goto out;
if (done) {
if (key == PROMPT_KEY_CLOSE) {
if (cdata->flags & PROMPT_INCREMENTAL)
goto out;
cmd_append_argv(&cdata->argc, &cdata->argv, s);
if (++cdata->current != cdata->count) {
prompt = &cdata->prompts[cdata->current];
status_prompt_update(c, prompt->prompt, prompt->input);
return (1);
if (cdata->wp != NULL) {
window_pane_update_prompt(cdata->wp,
prompt->prompt, prompt->input);
} else
status_prompt_update(c, prompt->prompt,
prompt->input);
return (PROMPT_CONTINUE);
}
}
argc = cdata->argc;
argv = cmd_copy_argv(cdata->argc, cdata->argv);
if (!done)
if (key != PROMPT_KEY_CLOSE)
cmd_append_argv(&argc, &argv, s);
if (done) {
else {
cmd_free_argv(cdata->argc, cdata->argv);
cdata->argc = argc;
cdata->argv = cmd_copy_argv(argc, argv);
@@ -226,13 +248,20 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
}
cmd_free_argv(argc, argv);
if (c->prompt_inputcb != cmd_command_prompt_callback)
return (1);
/*
* An incremental prompt fires its callback on every edit but must stay
* open for further typing; only an explicit close (handled above) ends
* it.
*/
if (cdata->flags & PROMPT_INCREMENTAL)
return (PROMPT_CONTINUE);
out:
if (item != NULL)
if (item != NULL) {
cdata->item = NULL;
cmdq_continue(item);
return (0);
}
return (PROMPT_CLOSE);
}
static void
@@ -241,6 +270,11 @@ cmd_command_prompt_free(void *data)
struct cmd_command_prompt_cdata *cdata = data;
u_int i;
if (cdata->item != NULL) {
cmdq_continue(cdata->item);
cdata->item = NULL;
}
for (i = 0; i < cdata->count; i++) {
free(cdata->prompts[i].prompt);
free(cdata->prompts[i].input);

View File

@@ -33,8 +33,8 @@ static enum args_parse_type cmd_confirm_before_args_parse(struct args *,
static enum cmd_retval cmd_confirm_before_exec(struct cmd *,
struct cmdq_item *);
static int cmd_confirm_before_callback(struct client *, void *,
const char *, int);
static enum prompt_result cmd_confirm_before_callback(struct client *, void *,
const char *, enum prompt_key_result);
static void cmd_confirm_before_free(void *);
const struct cmd_entry cmd_confirm_before_entry = {
@@ -118,9 +118,9 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_WAIT);
}
static int
static enum prompt_result
cmd_confirm_before_callback(struct client *c, void *data, const char *s,
__unused int done)
__unused enum prompt_key_result key)
{
struct cmd_confirm_before_data *cdata = data;
struct cmdq_item *item = cdata->item, *new_item;
@@ -151,7 +151,7 @@ out:
cmdq_get_client(item)->retval = retcode;
cmdq_continue(item);
}
return (0);
return (PROMPT_CLOSE);
}
static void

View File

@@ -48,6 +48,16 @@ struct cmd_display_panes_data {
struct args_command_state *state;
};
struct cmd_display_panes_ctx {
struct client *c;
int ox;
int oy;
u_int sx;
u_int sy;
u_int statuslines;
int statustop;
};
static enum args_parse_type
cmd_display_panes_args_parse(__unused struct args *args, __unused u_int idx,
__unused char **cause)
@@ -56,7 +66,7 @@ cmd_display_panes_args_parse(__unused struct args *args, __unused u_int idx,
}
static void
cmd_display_panes_put(struct screen_redraw_ctx *ctx,
cmd_display_panes_put(struct cmd_display_panes_ctx *ctx,
struct window_pane *wp, u_int cx, u_int cy, const char *buf, size_t len)
{
struct client *c = ctx->c;
@@ -76,7 +86,7 @@ cmd_display_panes_put(struct screen_redraw_ctx *ctx,
}
static void
cmd_display_panes_draw_format(struct screen_redraw_ctx *ctx,
cmd_display_panes_draw_format(struct cmd_display_panes_ctx *ctx,
struct window_pane *wp, u_int xoff, u_int yoff, u_int sx,
const struct grid_cell *gc)
{
@@ -112,7 +122,7 @@ cmd_display_panes_draw_format(struct screen_redraw_ctx *ctx,
}
static void
cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
cmd_display_panes_draw_pane(struct cmd_display_panes_ctx *ctx,
struct window_pane *wp)
{
struct client *c = ctx->c;
@@ -123,7 +133,8 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
struct grid_cell fgc, bgc;
u_int pane, idx, px, py, i, j, xoff, yoff, sx, sy;
u_int cx, cy;
int colour, active_colour;
const char *name;
struct format_tree *ft;
char buf[16], lbuf[16], *ptr;
size_t len, llen;
@@ -181,18 +192,16 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
if (sx < len)
return;
colour = options_get_number(oo, "display-panes-colour");
active_colour = options_get_number(oo, "display-panes-active-colour");
if (w->active == wp)
name = "display-panes-active-colour";
else
name = "display-panes-colour";
ft = format_create_defaults(NULL, c, s, NULL, wp);
style_apply(&fgc, oo, name, ft);
format_free(ft);
memcpy(&fgc, &grid_default_cell, sizeof fgc);
memcpy(&bgc, &grid_default_cell, sizeof bgc);
if (w->active == wp) {
fgc.fg = active_colour;
bgc.bg = active_colour;
} else {
fgc.fg = colour;
bgc.bg = colour;
}
bgc.bg = fgc.fg;
if (pane > 9 && pane < 35)
llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10));
@@ -254,17 +263,30 @@ out:
}
static void
cmd_display_panes_draw(struct client *c, __unused void *data,
struct screen_redraw_ctx *ctx)
cmd_display_panes_draw(struct client *c, __unused void *data)
{
struct window *w = c->session->curw->window;
struct window_pane *wp;
struct session *s = c->session;
struct window *w = s->curw->window;
struct window_pane *wp;
struct cmd_display_panes_ctx ctx;
u_int lines;
log_debug("%s: %s @%u", __func__, c->name, w->id);
memset(&ctx, 0, sizeof ctx);
ctx.c = c;
tty_window_offset(&c->tty, &ctx.ox, &ctx.oy, &ctx.sx, &ctx.sy);
if (options_get_number(s->options, "status-position") == 0) {
lines = status_line_size(c);
if (c->message_string != NULL || c->prompt != NULL)
lines = (lines == 0 ? 1 : lines);
ctx.statuslines = lines;
ctx.statustop = 1;
}
TAILQ_FOREACH(wp, &w->panes, entry) {
if (window_pane_is_visible(wp))
cmd_display_panes_draw_pane(ctx, wp);
cmd_display_panes_draw_pane(&ctx, wp);
}
}

View File

@@ -202,24 +202,29 @@ cmd_join_pane_move(struct cmdq_item *item, struct args *args,
char *cause = NULL, flag;
int xoff = lc->xoff, yoff = lc->yoff, adjust;
u_int i;
enum pane_lines lines = window_pane_get_pane_lines(wp);
if (args_has(args, 'X')) {
xoff = args_percentage_and_expand(args, 'X', -(int)lc->sx,
xoff = args_percentage_and_expand(args, 'X', -(int)w->sx,
w->sx, w->sx, item, &cause);
if (cause != NULL) {
cmdq_error(item, "position %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
if (lines != PANE_LINES_NONE)
xoff += 1;
}
if (args_has(args, 'Y')) {
yoff = args_percentage_and_expand(args, 'Y', -(int)lc->sy,
yoff = args_percentage_and_expand(args, 'Y', -(int)w->sy,
w->sy, w->sy, item, &cause);
if (cause != NULL) {
cmdq_error(item, "position %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
if (lines != PANE_LINES_NONE)
yoff += 1;
}
for (i = 0; i < nitems(flags); i++) {

View File

@@ -101,22 +101,22 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if ((tmp = args_get(args, 'n')) != NULL) {
ename = format_single(item, tmp, c, NULL, NULL, NULL);
if (!check_name(ename, WINDOW_NAME_FORBID)) {
if (!check_name(ename)) {
cmdq_error(item, "invalid window name: %s", ename);
free(ename);
return (CMD_RETURN_ERROR);
}
wname = clean_name(ename, WINDOW_NAME_FORBID);
wname = clean_name(ename, 0);
free(ename);
}
if ((tmp = args_get(args, 's')) != NULL) {
ename = format_single(item, tmp, c, NULL, NULL, NULL);
if (!check_name(ename, SESSION_NAME_FORBID)) {
if (!check_name(ename)) {
cmdq_error(item, "invalid session name: %s", ename);
free(ename);
goto fail;
}
sname = clean_name(ename, SESSION_NAME_FORBID);
sname = clean_name(ename, 0);
free(ename);
}
if (args_has(args, 'A')) {
@@ -152,12 +152,12 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
else if (groupwith != NULL)
prefix = xstrdup(groupwith->name);
else {
if (!check_name(group, SESSION_NAME_FORBID)) {
if (!check_name(group)) {
cmdq_error(item,
"invalid session group name: %s", group);
goto fail;
}
prefix = clean_name(group, SESSION_NAME_FORBID);
prefix = clean_name(group, 0);
}
}

View File

@@ -73,12 +73,12 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
name = args_get(args, 'n');
if (name != NULL) {
expanded = format_single(item, name, c, s, NULL, NULL);
if (!check_name(expanded, WINDOW_NAME_FORBID)) {
if (!check_name(expanded)) {
cmdq_error(item, "invalid window name: %s", expanded);
free(expanded);
return (CMD_RETURN_ERROR);
}
wname = clean_name(expanded, WINDOW_NAME_FORBID);
wname = clean_name(expanded, 0);
free(expanded);
}
if (args_has(args, 'S') && wname != NULL && target->idx == -1) {

View File

@@ -52,12 +52,12 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item)
char *newname, *tmp;
tmp = format_single_from_target(item, args_string(args, 0));
if (!check_name(tmp, SESSION_NAME_FORBID)) {
if (!check_name(tmp)) {
cmdq_error(item, "invalid session name: %s", tmp);
free(tmp);
return (CMD_RETURN_ERROR);
}
newname = clean_name(tmp, SESSION_NAME_FORBID);
newname = clean_name(tmp, 0);
free(tmp);
if (strcmp(newname, s->name) == 0) {
free(newname);

View File

@@ -51,13 +51,13 @@ cmd_rename_window_exec(struct cmd *self, struct cmdq_item *item)
char *name;
name = format_single_from_target(item, args_string(args, 0));
if (!check_name(name, WINDOW_NAME_FORBID)) {
if (!check_name(name)) {
cmdq_error(item, "invalid window name: %s", name);
free(name);
return (CMD_RETURN_ERROR);
}
window_set_name(wl->window, name, WINDOW_NAME_FORBID);
window_set_name(wl->window, name, 0);
options_set_number(wl->window->options, "automatic-rename", 0);
free(name);

View File

@@ -18,6 +18,7 @@
#include <sys/types.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
@@ -63,8 +64,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
const char *errstr, *argval;
const char flags[4] = { 'U', 'D', 'L', 'R' };
char *cause = NULL, flag;
u_int opposite = 0;
int adjust, x, y, status;
int adjust, x, y, status, opposite = 0;
long unsigned i;
struct grid *gd = wp->base.grid;
@@ -94,16 +94,15 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
server_unzoom_window(w);
if (args_has(args, 'x')) {
x = args_percentage(args, 'x', 0, INT_MAX, w->sx, &cause);
x = args_percentage(args, 'x', 0, PANE_MAXIMUM, w->sx, &cause);
if (cause != NULL) {
cmdq_error(item, "width %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
if (window_pane_is_floating(wp)) {
layout_resize_floating_pane_to(wp, LAYOUT_LEFTRIGHT, x,
&cause);
if (cause != NULL) {
if (layout_resize_floating_pane_to(wp, LAYOUT_LEFTRIGHT,
x, &cause) != 0) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
@@ -112,7 +111,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x);
}
if (args_has(args, 'y')) {
y = args_percentage(args, 'y', 0, INT_MAX, w->sy, &cause);
y = args_percentage(args, 'y', 0, PANE_MAXIMUM, w->sy, &cause);
if (cause != NULL) {
cmdq_error(item, "height %s", cause);
free(cause);
@@ -130,9 +129,8 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
break;
}
if (window_pane_is_floating(wp)) {
layout_resize_floating_pane_to(wp, LAYOUT_TOPBOTTOM, y,
&cause);
if (cause != NULL) {
if (layout_resize_floating_pane_to(wp, LAYOUT_TOPBOTTOM,
y, &cause) != 0) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
@@ -147,8 +145,12 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
continue;
argval = args_get(args, flag);
if (argval == NULL)
argval = "1";
if (argval == NULL) {
if (args_count(args) == 0)
argval = "1";
else
argval = args_string(args, 0);
}
adjust = strtonum(argval, INT_MIN, INT_MAX, &errstr);
if (errstr != NULL) {
@@ -164,9 +166,8 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
if (flag == 'L' || flag == 'U')
opposite = 1;
layout_resize_floating_pane(wp, type, adjust, opposite,
&cause);
if (cause != NULL) {
if (layout_resize_floating_pane(wp, type, adjust,
opposite, &cause) != 0) {
cmdq_error(item, "adjustment %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
@@ -234,7 +235,7 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
struct window_pane *wp;
struct layout_cell *lc;
int y, ly, x, lx, sx, sy, new_sx, new_sy;
int scrollbars, sb_pos, left, right;
int left, right;
int new_xoff, new_yoff, resizes = 0;
wp = cmd_mouse_pane(m, NULL, &wl);
@@ -246,15 +247,13 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
lc = wp->layout_cell;
sx = wp->sx;
sy = wp->sy;
scrollbars = options_get_number(w->options, "pane-scrollbars");
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
left = wp->xoff - 1;
right = wp->xoff + sx;
if (window_pane_show_scrollbar(wp, scrollbars) &&
sb_pos == PANE_SCROLLBARS_LEFT) {
if (window_pane_scrollbar_reserve(wp) &&
w->sb_pos == PANE_SCROLLBARS_LEFT) {
left -= wp->scrollbar_style.width + wp->scrollbar_style.pad;
} else if (window_pane_show_scrollbar(wp, scrollbars) &&
sb_pos == PANE_SCROLLBARS_RIGHT) {
} else if (window_pane_scrollbar_reserve(wp) &&
w->sb_pos == PANE_SCROLLBARS_RIGHT) {
right += wp->scrollbar_style.width + wp->scrollbar_style.pad;
}

View File

@@ -109,6 +109,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item)
window_set_active_pane(w, wp, 1);
cmd_find_from_winlink_pane(current, wl, wp, 0);
window_pop_zoom(w);
redraw_invalidate_scene(w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL);

View File

@@ -55,56 +55,44 @@ cmd_show_prompt_history_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = cmd_get_args(self);
const char *typestr = args_get(args, 'T');
enum prompt_type type;
u_int tidx, hidx;
u_int t, h;
const char *v;
if (cmd_get_entry(self) == &cmd_clear_prompt_history_entry) {
if (typestr == NULL) {
for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) {
for (hidx = 0; hidx < status_prompt_hsize[tidx];
hidx++)
free(status_prompt_hlist[tidx][hidx]);
free(status_prompt_hlist[tidx]);
status_prompt_hlist[tidx] = NULL;
status_prompt_hsize[tidx] = 0;
}
for (t = 0; t < PROMPT_NTYPES; t++)
prompt_history_clear(t);
} else {
type = status_prompt_type(typestr);
type = prompt_type(typestr);
if (type == PROMPT_TYPE_INVALID) {
cmdq_error(item, "invalid type: %s", typestr);
return (CMD_RETURN_ERROR);
}
for (hidx = 0; hidx < status_prompt_hsize[type]; hidx++)
free(status_prompt_hlist[type][hidx]);
free(status_prompt_hlist[type]);
status_prompt_hlist[type] = NULL;
status_prompt_hsize[type] = 0;
prompt_history_clear(type);
}
return (CMD_RETURN_NORMAL);
}
if (typestr == NULL) {
for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) {
cmdq_print(item, "History for %s:\n",
status_prompt_type_string(tidx));
for (hidx = 0; hidx < status_prompt_hsize[tidx];
hidx++) {
cmdq_print(item, "%d: %s", hidx + 1,
status_prompt_hlist[tidx][hidx]);
for (t = 0; t < PROMPT_NTYPES; t++) {
typestr = prompt_type_string(t);
cmdq_print(item, "History for %s:\n", typestr);
for (h = 0; h < prompt_history_size(t); h++) {
v = prompt_history_get(t, h);
cmdq_print(item, "%d: %s", h + 1, v);
}
cmdq_print(item, "%s", "");
}
} else {
type = status_prompt_type(typestr);
type = prompt_type(typestr);
if (type == PROMPT_TYPE_INVALID) {
cmdq_error(item, "invalid type: %s", typestr);
return (CMD_RETURN_ERROR);
}
cmdq_print(item, "History for %s:\n",
status_prompt_type_string(type));
for (hidx = 0; hidx < status_prompt_hsize[type]; hidx++) {
cmdq_print(item, "%d: %s", hidx + 1,
status_prompt_hlist[type][hidx]);
cmdq_print(item, "History for %s:\n", prompt_type_string(type));
for (h = 0; h < prompt_history_size(type); h++) {
v = prompt_history_get(type, h);
cmdq_print(item, "%d: %s", h + 1, v);
}
cmdq_print(item, "%s", "");
}

View File

@@ -40,8 +40,8 @@ const struct cmd_entry cmd_new_pane_entry = {
.args = { "bB:c:de:EfF:hIkl:Lm:p:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
.usage = "[-bdefhIklPvWZ] [-B border-lines] "
"[-c start-directory] [-e environment] "
"[-F format] [-l size] [-m message] [-p percentage] "
"[-c start-directory] [-e environment] "
"[-F format] [-l size] [-m message] [-p percentage] "
"[-s style] [-S active-border-style] "
"[-R inactive-border-style] [-T title] [-x width] [-y height] "
"[-X x-position] [-Y y-position] " CMD_TARGET_PANE_USAGE " "
@@ -87,7 +87,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
int input, empty, is_floating, flags = 0;
const char *template, *style, *value;
char *cause = NULL, *cp, *title;
struct options_entry *oe;
const struct options_table_entry *oe;
struct args_value *av;
enum pane_lines lines;
u_int count = args_count(args);
@@ -95,7 +95,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if (cmd_get_entry(self) == &cmd_new_pane_entry)
is_floating = !args_has(args, 'L');
else
is_floating = 0;
is_floating = window_pane_is_floating(wp);
flags = is_floating ? SPAWN_FLOATING : 0;
if (args_has(args, 'b'))
@@ -117,8 +117,20 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if (empty)
flags |= SPAWN_EMPTY;
if ((value = args_get(args, 'B')) == NULL)
lines = window_get_pane_lines(w);
else {
oe = options_search("pane-border-lines");
lines = options_find_choice(oe, value, &cause);
if (cause != NULL) {
cmdq_error(item, "pane-border-lines %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
if (is_floating)
lc = layout_get_floating_cell(item, args, w, wp, &cause);
lc = layout_get_floating_cell(item, args, lines, w, wp, &cause);
else
lc = layout_get_tiled_cell(item, args, w, wp, flags, &cause);
if (cause != NULL) {
@@ -155,10 +167,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if ((new_wp = spawn_pane(&sc, &cause)) == NULL) {
cmdq_error(item, "create pane failed: %s", cause);
free(cause);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_ERROR);
goto fail;
}
style = args_get(args, 's');
@@ -166,7 +175,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if (options_set_string(new_wp->options, "window-style", 0,
"%s", style) == NULL) {
cmdq_error(item, "bad style: %s", style);
return (CMD_RETURN_ERROR);
goto fail;
}
options_set_string(new_wp->options, "window-active-style", 0,
"%s", style);
@@ -178,7 +187,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if (options_set_string(new_wp->options,
"pane-active-border-style", 0, "%s", style) == NULL) {
cmdq_error(item, "bad active border style: %s", style);
return (CMD_RETURN_ERROR);
goto fail;
}
}
style = args_get(args, 'R');
@@ -187,22 +196,11 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
"%s", style) == NULL) {
cmdq_error(item, "bad inactive border style: %s",
style);
return (CMD_RETURN_ERROR);
goto fail;
}
}
value = args_get(args, 'B');
if (value != NULL) {
oe = options_get(new_wp->options, "pane-border-lines");
lines = options_find_choice(options_table_entry(oe), value,
&cause);
if (cause != NULL) {
cmdq_error(item, "pane-border-lines %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
options_set_number(new_wp->options, "pane-border-lines",
lines);
}
if (args_has(args, 'B'))
options_set_number(new_wp->options, "pane-border-lines", lines);
if (args_has(args, 'k') || args_has(args, 'm')) {
options_set_number(new_wp->options, "remain-on-exit", 3);
if (args_has(args, 'm')) {
@@ -227,10 +225,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
window_remove_pane(wp->window, new_wp);
cmdq_error(item, "%s", cause);
free(cause);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_ERROR);
goto fail;
case 1:
input = 0;
break;
@@ -272,4 +267,13 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_WAIT);
}
return (CMD_RETURN_NORMAL);
fail:
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
layout_destroy_cell(w, lc, &w->layout_root);
return (CMD_RETURN_ERROR);
}

View File

@@ -143,9 +143,11 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
colour_palette_from_option(&src_wp->palette, src_wp->options);
colour_palette_from_option(&dst_wp->palette, dst_wp->options);
layout_fix_panes(src_w, NULL);
redraw_invalidate_scene(src_w);
server_redraw_window(src_w);
}
layout_fix_panes(dst_w, NULL);
redraw_invalidate_scene(dst_w);
server_redraw_window(dst_w);
notify_window("window-layout-changed", src_w);

2
cmd.c
View File

@@ -117,6 +117,7 @@ extern const struct cmd_entry cmd_suspend_client_entry;
extern const struct cmd_entry cmd_swap_pane_entry;
extern const struct cmd_entry cmd_swap_window_entry;
extern const struct cmd_entry cmd_switch_client_entry;
extern const struct cmd_entry cmd_switch_mode_entry;
extern const struct cmd_entry cmd_unbind_key_entry;
extern const struct cmd_entry cmd_unlink_window_entry;
extern const struct cmd_entry cmd_wait_for_entry;
@@ -212,6 +213,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_swap_pane_entry,
&cmd_swap_window_entry,
&cmd_switch_client_entry,
&cmd_switch_mode_entry,
&cmd_unbind_key_entry,
&cmd_unlink_window_entry,
&cmd_wait_for_entry,

View File

@@ -26,6 +26,85 @@
#include "tmux.h"
/* Theme colour slots and their server options. */
static const struct {
const char *name;
const char *dark_option;
const char *light_option;
int terminal_colour;
} colour_theme_table[] = {
{ "themeblack",
"dark-theme-black",
"light-theme-black",
0
},
{ "themewhite",
"dark-theme-white",
"light-theme-white",
7
},
{ "themelightgrey",
"dark-theme-light-grey",
"light-theme-light-grey",
7
},
{ "themedarkgrey",
"dark-theme-dark-grey",
"light-theme-dark-grey",
0
},
{ "themegreen",
"dark-theme-green",
"light-theme-green",
2
},
{ "themeyellow",
"dark-theme-yellow",
"light-theme-yellow",
3
},
{ "themered",
"dark-theme-red",
"light-theme-red",
1
},
{ "themeblue",
"dark-theme-blue",
"light-theme-blue",
4
},
{ "themecyan",
"dark-theme-cyan",
"light-theme-cyan",
6
},
{ "thememagenta",
"dark-theme-magenta",
"light-theme-magenta",
5
}
};
/* Get theme colour option. */
const char *
colour_theme_option(u_int n, enum client_theme theme)
{
if (n >= nitems(colour_theme_table))
return (NULL);
if (theme == THEME_LIGHT)
return (colour_theme_table[n].light_option);
return (colour_theme_table[n].dark_option);
}
/* Get theme terminal colour. */
int
colour_theme_terminal_colour(u_int n)
{
if (n >= nitems(colour_theme_table))
return (8);
return (colour_theme_table[n].terminal_colour);
}
static int
colour_dist_sq(int R, int G, int B, int r, int g, int b)
{
@@ -126,7 +205,7 @@ colour_dim(int c, u_int dim)
{
u_char r, g, b;
if (dim == 0 || COLOUR_DEFAULT(c))
if (dim == 0 || COLOUR_DEFAULT(c) || (c & COLOUR_FLAG_THEME))
return (c);
if (dim >= 100)
return (colour_join_rgb(0, 0, 0));
@@ -152,6 +231,13 @@ colour_tostring(int c)
if (c == -1)
return ("none");
if (c & COLOUR_FLAG_THEME) {
c &= 0xff;
if (c >= 0 && (u_int)c < nitems(colour_theme_table))
return (colour_theme_table[c].name);
return ("invalid");
}
if (c & COLOUR_FLAG_RGB) {
colour_split_rgb(c, &r, &g, &b);
xsnprintf(s, sizeof s, "#%02x%02x%02x", r, g, b);
@@ -252,6 +338,7 @@ colour_fromstring(const char *s)
const char *cp;
int n;
u_char r, g, b;
u_int i;
if (*s == '#' && strlen(s) == 7) {
for (cp = s + 1; isxdigit((u_char) *cp); cp++)
@@ -282,6 +369,11 @@ colour_fromstring(const char *s)
if (strcasecmp(s, "terminal") == 0)
return (9);
for (i = 0; i < nitems(colour_theme_table); i++) {
if (strcasecmp(s, colour_theme_table[i].name) == 0)
return (i|COLOUR_FLAG_THEME);
}
if (strcasecmp(s, "black") == 0 || strcmp(s, "0") == 0)
return (0);
if (strcasecmp(s, "red") == 0 || strcmp(s, "1") == 0)

View File

@@ -710,6 +710,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
"AFTER" };
size_t size = strlen(expanded);
struct screen *os = octx->s, s[TOTAL];
struct hyperlinks *hl = os->hyperlinks;
struct screen_write_ctx ctx[TOTAL];
u_int ocx = os->cx, ocy = os->cy, n, i, width[TOTAL];
u_int map[] = { LEFT,
@@ -723,7 +724,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
struct grid_cell gc, current_default, base_default;
struct style sy, saved_sy;
struct utf8_data *ud = &sy.gc.data;
const char *cp, *end;
const char *cp, *end, *link_uri;
enum utf8_state more;
char *tmp;
struct format_range *fr = NULL, *fr1;
@@ -838,6 +839,17 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
sy.gc.fg = base->fg;
}
/*
* Resolve any hyperlink and store it in the cell. The URI
* doubles as the internal ID so repeated links share one entry
* and the ID stays stable across redraws.
*/
link_uri = style_link(&sy);
if (link_uri != NULL && hl != NULL)
sy.gc.link = hyperlinks_put(hl, link_uri, link_uri);
else
sy.gc.link = 0;
/* If this style has a fill colour, store it for later. */
if (sy.fill != 8)
fill = sy.fill;

View File

@@ -1477,6 +1477,33 @@ format_cb_client_cell_width(struct format_tree *ft)
return (NULL);
}
/* Callback for client_colours. */
static void *
format_cb_client_colours(struct format_tree *ft)
{
struct tty_term *term;
u_int colours;
if (ft->c == NULL || (~ft->c->tty.flags & TTY_STARTED))
return (NULL);
term = ft->c->tty.term;
if (term->flags & TERM_RGBCOLOURS)
colours = 16777216;
else if (term->flags & TERM_256COLOURS)
colours = 256;
else {
colours = tty_term_number(term, TTYC_COLORS);
if (colours < 8)
colours = 2;
else if (colours < 16)
colours = 8;
else
colours = 16;
}
return (format_printf("%u", colours));
}
/* Callback for client_control_mode. */
static void *
format_cb_client_control_mode(struct format_tree *ft)
@@ -3228,6 +3255,9 @@ static const struct format_table_entry format_table[] = {
{ "client_cell_width", FORMAT_TABLE_STRING,
format_cb_client_cell_width
},
{ "client_colours", FORMAT_TABLE_STRING,
format_cb_client_colours
},
{ "client_control_mode", FORMAT_TABLE_STRING,
format_cb_client_control_mode
},
@@ -4542,6 +4572,47 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
return (list);
}
/* Match using the fuzzy matcher. */
static char *
format_match_fuzzy(const char *pattern, const char *text, int positions)
{
struct evbuffer *buffer;
bitstr_t *bs;
char *value;
size_t size;
u_int i, width;
width = format_width(text);
if (width == 0)
width = 1;
bs = fuzzy_match(pattern, text, width, NULL);
if (bs == NULL)
return (xstrdup(positions ? "" : "0"));
if (!positions) {
free(bs);
return (xstrdup("1"));
}
buffer = evbuffer_new();
if (buffer == NULL)
fatalx("out of memory");
for (i = 0; i < width; i++) {
if (!bit_test(bs, i))
continue;
if (EVBUFFER_LENGTH(buffer) != 0)
evbuffer_add(buffer, ",", 1);
evbuffer_add_printf(buffer, "%u", i);
}
if ((size = EVBUFFER_LENGTH(buffer)) != 0)
value = xmemdup(EVBUFFER_DATA(buffer), size);
else
value = xstrdup("");
evbuffer_free(buffer);
free(bs);
return (value);
}
/* Match against an fnmatch(3) pattern or regular expression. */
static char *
format_match(struct format_modifier *fm, const char *pattern, const char *text)
@@ -4552,6 +4623,10 @@ format_match(struct format_modifier *fm, const char *pattern, const char *text)
if (fm->argc >= 1)
s = fm->argv[0];
if (strchr(s, 'p') != NULL)
return (format_match_fuzzy(pattern, text, 1));
if (strchr(s, 'z') != NULL)
return (format_match_fuzzy(pattern, text, 0));
if (strchr(s, 'r') == NULL) {
if (strchr(s, 'i') != NULL)
flags |= FNM_CASEFOLD;

655
fuzzy.c Normal file
View File

@@ -0,0 +1,655 @@
/* $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 <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* Fuzzy matching in the style of fzf. The pattern is split into groups by |
* and each group is split on spaces into terms. A row matches if any group
* matches; within a group all positive terms must match and all inverse terms
* must not match.
*
* Plain positive terms are fuzzy subsequences. A leading ' makes a term an
* exact substring match, ^ anchors a term at the start and $ anchors it at
* the end. A leading ! inverts the term. Plain inverse terms are exact
* substring matches rather than inverse fuzzy matches, like fzf.
*
* Both the pattern and the text are UTF-8. The text may contain tmux style
* directives (#[...]); these and their contents are invisible to matching and
* occupy no columns, but align= styles do move the surrounding text and are
* accounted for exactly as format_draw lays it out (the no-list layout, see
* format_draw_none). Matching is smart-case: case is ignored unless the pattern
* contains an uppercase character (ASCII case folding only; other characters
* are compared exactly by their UTF-8 data).
*
* On a match a bitstr_t of the requested display width is returned with a bit
* set for every column occupied by a matched character, so the caller can
* highlight them; NULL is returned if there is no match. A cheap fzf-style
* score (matches at the start, after word boundaries and in contiguous runs
* score higher) is also produced so callers can rank best-match-first.
*/
#define FUZZY_BONUS_EXACT 1000
#define FUZZY_BONUS_PREFIX 200
#define FUZZY_BONUS_SUFFIX 100
#define FUZZY_BONUS_START 12
#define FUZZY_BONUS_BOUNDARY 8
#define FUZZY_BONUS_CONSECUTIVE 6
#define FUZZY_PENALTY_LEADING 1
#define FUZZY_PENALTY_LEADING_MAX 10
#define FUZZY_PENALTY_GAP 1
/* A single visible character of the text. */
struct fuzzy_char {
enum style_align align;
struct utf8_data ud; /* original UTF-8 data */
u_int width; /* display width */
u_int offset; /* within its alignment */
};
/* One parsed query term. */
struct fuzzy_term {
int inverse;
int exact;
int prefix;
int suffix;
const char *text;
size_t len;
};
/* Is this character a word boundary, so a match after it scores higher? */
static int
fuzzy_is_boundary(const struct utf8_data *ud)
{
static const char *boundary = " -_/.:";
if (ud->size != 1)
return (0);
return (strchr(boundary, ud->data[0]) != NULL);
}
/*
* Compare two characters, folding ASCII case if wanted. UTF-8 is compared
* directly without case folding.
*/
static int
fuzzy_char_equal(const struct utf8_data *a, const struct utf8_data *b, int fold)
{
if (fold &&
a->size == 1 &&
b->size == 1 &&
a->data[0] < 0x80 &&
b->data[0] < 0x80)
return (tolower(a->data[0]) == tolower(b->data[0]));
return (a->size == b->size && memcmp(a->data, b->data, a->size) == 0);
}
/* Map a style alignment onto one of the four layout columns. */
static enum style_align
fuzzy_align(enum style_align align)
{
if (align == STYLE_ALIGN_DEFAULT)
return (STYLE_ALIGN_LEFT);
return (align);
}
/* Add a visible character to the array, updating the alignment width. */
static void
fuzzy_add(struct fuzzy_char **cs, u_int *ncs, u_int *alloc, enum style_align a,
const struct utf8_data *ud, u_int *widths)
{
struct fuzzy_char *fc;
if (*ncs == *alloc) {
*alloc = (*alloc == 0) ? 64 : *alloc * 2;
*cs = xreallocarray(*cs, *alloc, sizeof **cs);
}
fc = &(*cs)[(*ncs)++];
fc->align = a;
memcpy(&fc->ud, ud, sizeof fc->ud);
fc->width = ud->width;
fc->offset = widths[a];
widths[a] += ud->width;
}
/* Decode a character as UTF-8. */
static const char *
fuzzy_decode_one(const char *cp, const char *end, struct utf8_data *ud)
{
enum utf8_state more;
const char *start = cp;
if ((more = utf8_open(ud, (u_char)*cp)) == UTF8_MORE) {
while (++cp != end && more == UTF8_MORE)
more = utf8_append(ud, (u_char)*cp);
if (more == UTF8_DONE)
return (cp);
cp = start;
}
utf8_set(ud, (u_char)*cp);
return (cp + 1);
}
/*
* Scan the text into an array of visible characters, skipping styles and
* recording the alignment and intra-alignment offset of each. Returns the
* array and its length and fills in the per-alignment widths.
*/
static struct fuzzy_char *
fuzzy_scan(const char *text, u_int *ncs, u_int *widths)
{
struct fuzzy_char *cs = NULL;
u_int alloc = 0, n, leading, i;
enum style_align current = STYLE_ALIGN_LEFT;
struct style sy;
const char *cp = text, *textend = text + strlen(text);
const char *end;
struct utf8_data ud, hash, bracket;
char *tmp;
*ncs = 0;
memset(widths, 0, sizeof *widths * (STYLE_ALIGN_ABSOLUTE_CENTRE + 1));
style_set(&sy, &grid_default_cell);
utf8_set(&hash, '#');
utf8_set(&bracket, '[');
while (*cp != '\0') {
/* Handle a run of #s, which may introduce a style. */
if (*cp == '#') {
for (n = 0; cp[n] == '#'; n++)
/* nothing */;
if (cp[n] != '[') {
/* Escaped #s: ##->#, so half (rounded up). */
leading = (n % 2 == 0) ? n / 2 : n / 2 + 1;
for (i = 0; i < leading; i++) {
fuzzy_add(&cs, ncs, &alloc, current,
&hash, widths);
}
cp += n;
continue;
}
/* Even count: all #s escaped, the [ is literal. */
for (i = 0; i < n / 2; i++)
fuzzy_add(&cs, ncs, &alloc, current, &hash,
widths);
if (n % 2 == 0) {
fuzzy_add(&cs, ncs, &alloc, current, &bracket,
widths);
cp += n + 1;
continue;
}
/* Odd count: this is a style, find and parse it. */
end = format_skip(cp + n + 1, "]");
if (end == NULL)
break;
tmp = xstrndup(cp + n + 1, end - (cp + n + 1));
if (style_parse(&sy, &grid_default_cell, tmp) == 0)
current = fuzzy_align(sy.align);
free(tmp);
cp = end + 1;
continue;
}
/* Decode one character, multibyte or single byte. */
cp = fuzzy_decode_one(cp, textend, &ud);
/*
* Skip non-printable single bytes (control characters and raw
* bytes left over from a failed decode); keep printable ASCII
* and any decoded UTF-8.
*/
if (ud.size == 1 && (ud.data[0] <= 0x1f || ud.data[0] >= 0x7f))
continue;
fuzzy_add(&cs, ncs, &alloc, current, &ud, widths);
}
return (cs);
}
/*
* Work out the display column of a visible character given the trimmed widths
* and start columns of each alignment. Returns 0 and sets the column if the
* character is visible, otherwise returns -1.
*/
static int
fuzzy_column(const struct fuzzy_char *fc, const u_int *start, const u_int *src,
const u_int *vis, u_int *column)
{
enum style_align a = fc->align;
if (fc->offset < src[a] || fc->offset >= src[a] + vis[a])
return (-1);
*column = start[a] + (fc->offset - src[a]);
return (0);
}
/* Decode a UTF-8 term into an array of characters. */
static u_int
fuzzy_decode(const char *tok, size_t len, struct utf8_data *out)
{
const char *cp = tok, *end = tok + len;
u_int n = 0;
while (cp != end)
cp = fuzzy_decode_one(cp, end, &out[n++]);
return (n);
}
/* Add the score for a fuzzy token matched at the given positions. */
static int
fuzzy_score_positions(const u_int *pos, u_int npos, const struct fuzzy_char *cs)
{
u_int i, gap, span;
int score = 0;
if (npos == 0)
return (0);
if (pos[0] == 0)
score += FUZZY_BONUS_START;
else {
if (fuzzy_is_boundary(&cs[pos[0] - 1].ud))
score += FUZZY_BONUS_BOUNDARY;
if (pos[0] < FUZZY_PENALTY_LEADING_MAX)
score -= pos[0] * FUZZY_PENALTY_LEADING;
else {
score -= FUZZY_PENALTY_LEADING_MAX *
FUZZY_PENALTY_LEADING;
}
}
for (i = 1; i < npos; i++) {
if (pos[i] == pos[i - 1] + 1)
score += FUZZY_BONUS_CONSECUTIVE;
else if (fuzzy_is_boundary(&cs[pos[i] - 1].ud))
score += FUZZY_BONUS_BOUNDARY;
}
span = pos[npos - 1] - pos[0] + 1;
gap = span - npos;
score -= gap * FUZZY_PENALTY_GAP;
return (score);
}
/*
* Match a token as a subsequence of the visible characters. Returns if the
* token matches.
*/
static int
fuzzy_match_fuzzy(const struct utf8_data *tok, u_int toklen,
struct fuzzy_char *cs, u_int ncs, int fold, int *score, char *matched)
{
u_int pi, ci, *pos;
int found, value;
if (toklen == 0 || ncs == 0)
return (0);
pos = xcalloc(toklen, sizeof *pos);
/* First find a subsequence from the start. */
ci = 0;
for (pi = 0; pi < toklen; pi++) {
while (ci != ncs &&
!fuzzy_char_equal(&tok[pi], &cs[ci].ud, fold))
ci++;
if (ci == ncs) {
free(pos);
return (0);
}
pos[pi] = ci++;
}
/* Then compact it backwards to prefer a shorter span. */
ci = pos[toklen - 1];
for (pi = toklen; pi > 0; pi--) {
found = 0;
for (;;) {
if (fuzzy_char_equal(&tok[pi - 1], &cs[ci].ud, fold)) {
pos[pi - 1] = ci;
found = 1;
break;
}
if (ci == 0)
break;
ci--;
}
if (!found) {
free(pos);
return (0);
}
if (pi != 1)
ci--;
}
value = fuzzy_score_positions(pos, toklen, cs);
*score += value;
for (pi = 0; pi < toklen; pi++)
matched[pos[pi]] = 1;
free(pos);
return (1);
}
/* Score an exact, prefix or suffix match. */
static int
fuzzy_score_exact(u_int start, u_int toklen, u_int ncs,
const struct fuzzy_char *cs, int prefix, int suffix)
{
int score;
score = FUZZY_BONUS_EXACT + toklen * FUZZY_BONUS_CONSECUTIVE;
if (prefix)
score += FUZZY_BONUS_PREFIX;
if (suffix)
score += FUZZY_BONUS_SUFFIX;
if (start == 0)
score += FUZZY_BONUS_START;
else if (fuzzy_is_boundary(&cs[start - 1].ud))
score += FUZZY_BONUS_BOUNDARY;
if (start < FUZZY_PENALTY_LEADING_MAX)
score -= start * FUZZY_PENALTY_LEADING;
else
score -= FUZZY_PENALTY_LEADING_MAX * FUZZY_PENALTY_LEADING;
if (!prefix && !suffix)
score -= ncs - (start + toklen);
return (score);
}
/* Match an exact, prefix or suffix term against the visible characters. */
static int
fuzzy_match_exact(const struct utf8_data *tok, u_int toklen,
struct fuzzy_char *cs, u_int ncs, int fold, int prefix, int suffix,
int *score, char *matched)
{
u_int start, end, i, j, best = 0;
int ok, found = 0, value, bestscore = 0;
if (toklen == 0 || toklen > ncs)
return (0);
if (prefix && suffix) {
if (toklen != ncs)
return (0);
start = 0;
end = 1;
} else if (prefix) {
start = 0;
end = 1;
} else if (suffix) {
start = ncs - toklen;
end = start + 1;
} else {
start = 0;
end = ncs - toklen + 1;
}
for (i = start; i < end; i++) {
ok = 1;
for (j = 0; j < toklen; j++) {
if (!fuzzy_char_equal(&tok[j], &cs[i + j].ud, fold)) {
ok = 0;
break;
}
}
if (!ok)
continue;
value = fuzzy_score_exact(i, toklen, ncs, cs, prefix, suffix);
if (!found || value > bestscore) {
found = 1;
best = i;
bestscore = value;
}
}
if (!found)
return (0);
*score += bestscore;
if (matched != NULL) {
for (i = 0; i < toklen; i++)
matched[best + i] = 1;
}
return (1);
}
/* Parse one term. */
static int
fuzzy_parse_term(const char *start, const char *end, struct fuzzy_term *term)
{
memset(term, 0, sizeof *term);
if (start == end)
return (0);
if (*start == '!') {
term->inverse = 1;
start++;
}
if (start == end)
return (0);
if (*start == '\'') {
term->exact = 1;
start++;
} else if (*start == '^') {
term->exact = 1;
term->prefix = 1;
start++;
}
if (start == end)
return (0);
if (end[-1] == '$') {
term->exact = 1;
term->suffix = 1;
end--;
}
if (start == end)
return (0);
if (term->inverse)
term->exact = 1;
term->text = start;
term->len = end - start;
return (1);
}
/* Match one parsed term. */
static int
fuzzy_match_term(const struct fuzzy_term *term, struct utf8_data *tok,
struct fuzzy_char *cs, u_int ncs, int fold, int *score, char *matched)
{
u_int toklen;
int value = 0, matched_term;
toklen = fuzzy_decode(term->text, term->len, tok);
if (term->exact) {
matched_term = fuzzy_match_exact(tok, toklen, cs, ncs, fold,
term->prefix, term->suffix, &value,
term->inverse ? NULL : matched);
} else {
matched_term = fuzzy_match_fuzzy(tok, toklen, cs, ncs, fold,
&value, term->inverse ? NULL : matched);
}
if (term->inverse)
return (!matched_term);
if (!matched_term)
return (0);
*score += value;
return (1);
}
/* Match one AND group of terms. */
static int
fuzzy_match_group(const char *start, const char *end, struct utf8_data *tok,
struct fuzzy_char *cs, u_int ncs, int fold, int *score, char *matched)
{
const char *cp = start, *sp;
struct fuzzy_term term;
int any = 0;
*score = 0;
while (cp != end) {
while (cp != end && *cp == ' ')
cp++;
if (cp == end)
break;
sp = cp;
while (cp != end && *cp != ' ')
cp++;
if (!fuzzy_parse_term(sp, cp, &term))
return (0);
any = 1;
if (!fuzzy_match_term(&term, tok, cs, ncs, fold, score,
matched))
return (0);
}
return (any);
}
/*
* Fuzzy match pattern against text, which is drawn into a region of the given
* display width. Returns a bitstr_t of width bits with a bit set for each
* column occupied by a matched character, or NULL if there is no match. A
* higher returned score is better.
*/
bitstr_t *
fuzzy_match(const char *pattern, const char *text, u_int width, u_int *score)
{
struct fuzzy_char *cs;
char *matched = NULL, *best = NULL, *groupmatched;
struct utf8_data *tok;
bitstr_t *mask;
u_int ncs, i, j, column;
u_int widths[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
u_int start[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
u_int src[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
u_int vis[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
u_int wl, wc, wr, wa;
const char *cp, *sp;
int bestscore = 0, groupscore, found = 0, fold;
if (width == 0)
return (NULL);
/* An empty query matches everything, with nothing highlighted. */
for (cp = pattern; *cp == ' ' || *cp == '|'; cp++)
/* nothing */;
if (*cp == '\0') {
if (score != NULL)
*score = 0;
return (bit_alloc(width));
}
/* Smart-case: fold unless the pattern has an uppercase character. */
fold = 1;
for (cp = pattern; *cp != '\0'; cp++) {
if (*cp >= 'A' && *cp <= 'Z') {
fold = 0;
break;
}
}
/* Scan the text into visible characters. */
cs = fuzzy_scan(text, &ncs, widths);
matched = xcalloc(ncs == 0 ? 1 : ncs, sizeof *matched);
best = xcalloc(ncs == 0 ? 1 : ncs, sizeof *best);
tok = xreallocarray(NULL, strlen(pattern) + 1, sizeof *tok);
/* Match each |-separated group and keep the best-scoring one. */
cp = pattern;
while (*cp != '\0') {
while (*cp == ' ' || *cp == '|')
cp++;
if (*cp == '\0')
break;
sp = cp;
while (*cp != '\0' && *cp != '|')
cp++;
memset(matched, 0, ncs == 0 ? 1 : ncs);
groupmatched = matched;
if (fuzzy_match_group(sp, cp, tok, cs, ncs, fold,
&groupscore, groupmatched)) {
if (!found || groupscore > bestscore) {
found = 1;
bestscore = groupscore;
memcpy(best, matched, ncs == 0 ? 1 : ncs);
}
}
}
free(tok);
if (!found) {
free(best);
free(matched);
free(cs);
return (NULL);
}
/*
* Work out the trimmed widths and start columns of each alignment,
* mirroring format_draw_none.
*/
wl = widths[STYLE_ALIGN_LEFT];
wc = widths[STYLE_ALIGN_CENTRE];
wr = widths[STYLE_ALIGN_RIGHT];
wa = widths[STYLE_ALIGN_ABSOLUTE_CENTRE];
while (wl + wc + wr > width) {
if (wc > 0)
wc--;
else if (wr > 0)
wr--;
else
wl--;
}
if (wa > width)
wa = width;
start[STYLE_ALIGN_LEFT] = 0;
src[STYLE_ALIGN_LEFT] = 0;
vis[STYLE_ALIGN_LEFT] = wl;
start[STYLE_ALIGN_RIGHT] = width - wr;
src[STYLE_ALIGN_RIGHT] = widths[STYLE_ALIGN_RIGHT] - wr;
vis[STYLE_ALIGN_RIGHT] = wr;
start[STYLE_ALIGN_CENTRE] =
wl + ((width - wr) - wl) / 2 - wc / 2;
src[STYLE_ALIGN_CENTRE] = widths[STYLE_ALIGN_CENTRE] / 2 - wc / 2;
vis[STYLE_ALIGN_CENTRE] = wc;
start[STYLE_ALIGN_ABSOLUTE_CENTRE] = (width - wa) / 2;
src[STYLE_ALIGN_ABSOLUTE_CENTRE] = 0;
vis[STYLE_ALIGN_ABSOLUTE_CENTRE] = wa;
/* Set a bit for each column of each matched character. */
mask = bit_alloc(width);
for (i = 0; i < ncs; i++) {
if (!best[i])
continue;
if (fuzzy_column(&cs[i], start, src, vis, &column) != 0)
continue;
for (j = 0; j < cs[i].width && column + j < width; j++)
bit_set(mask, column + j);
}
free(best);
free(matched);
free(cs);
if (score != NULL)
*score = (bestscore < 0) ? 0 : (u_int)bestscore;
return (mask);
}

84
grid.c
View File

@@ -18,6 +18,9 @@
#include <sys/types.h>
#ifdef __APPLE__
#include <assert.h>
#endif
#include <stdlib.h>
#include <string.h>
@@ -56,6 +59,28 @@ static const struct grid_cell_entry grid_cleared_entry = {
{ .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED
};
#ifdef __APPLE__
static void
grid_check_lines(struct grid *gd)
{
u_int i, j;
for (i = 0; i < gd->hsize + gd->sy; i++) {
for (j = i + 1; j < gd->hsize + gd->sy; j++) {
if (gd->linedata[i].celldata != NULL)
assert(gd->linedata[i].celldata != gd->linedata[j].celldata);
if (gd->linedata[i].extddata != NULL)
assert(gd->linedata[i].extddata != gd->linedata[j].extddata);
}
}
}
#else
static void
grid_check_lines(__unused struct grid *gd)
{
}
#endif
/* Store cell in entry. */
static void
grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc,
@@ -86,7 +111,8 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
return (1);
if (gc->data.size > 1 || gc->data.width > 1)
return (1);
if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB))
if ((gc->fg & (COLOUR_FLAG_RGB|COLOUR_FLAG_THEME)) ||
(gc->bg & (COLOUR_FLAG_RGB|COLOUR_FLAG_THEME)))
return (1);
if (gc->us != 8) /* only supports 256 or RGB */
return (1);
@@ -218,7 +244,7 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg, int moved)
if (bg != 8)
gee->bg = bg;
} else if (bg != 8) {
if (bg & COLOUR_FLAG_RGB) {
if (bg & (COLOUR_FLAG_RGB|COLOUR_FLAG_THEME)) {
grid_get_extended_cell(gl, gce, gce->flags);
gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
gee->bg = bg;
@@ -286,9 +312,17 @@ grid_set_tab(struct grid_cell *gc, u_int width)
static void
grid_free_line(struct grid *gd, u_int py)
{
free(gd->linedata[py].celldata);
free(gd->linedata[py].extddata);
memset(&gd->linedata[py], 0, sizeof gd->linedata[py]);
struct grid_line *gl = &gd->linedata[py];
#ifdef __APPLE__
assert(gl->cellused <= gl->cellsize);
assert(gl->extdsize == 0 || gl->extddata != NULL);
assert(gl->cellsize == 0 || gl->celldata != NULL);
#endif
free(gl->celldata);
free(gl->extddata);
memset(gl, 0, sizeof *gl);
}
/* Free several lines. */
@@ -448,6 +482,8 @@ grid_scroll_history(struct grid *gd, u_int bg)
gd->linedata[gd->hsize].time = current_time;
gd->hsize++;
gd->scroll_added++;
grid_check_lines(gd);
}
/* Clear the history. */
@@ -497,6 +533,8 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
gd->hscrolled++;
gd->hsize++;
gd->scroll_added++;
grid_check_lines(gd);
}
/* Expand line to fit to cell. */
@@ -758,6 +796,8 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
}
if (py != 0 && (py < dy || py >= dy + ny))
gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
grid_check_lines(gd);
}
/* Move a group of cells. */
@@ -796,9 +836,16 @@ grid_string_cells_fg(const struct grid_cell *gc, int *values)
{
size_t n;
u_char r, g, b;
int c;
n = 0;
if (gc->fg & COLOUR_FLAG_256) {
if (gc->fg & COLOUR_FLAG_THEME) {
c = colour_theme_terminal_colour(gc->fg & 0xff);
if (c == 8)
values[n++] = 39;
else
values[n++] = c + 30;
} else if (gc->fg & COLOUR_FLAG_256) {
values[n++] = 38;
values[n++] = 5;
values[n++] = gc->fg & 0xff;
@@ -845,9 +892,16 @@ grid_string_cells_bg(const struct grid_cell *gc, int *values)
{
size_t n;
u_char r, g, b;
int c;
n = 0;
if (gc->bg & COLOUR_FLAG_256) {
if (gc->bg & COLOUR_FLAG_THEME) {
c = colour_theme_terminal_colour(gc->bg & 0xff);
if (c == 8)
values[n++] = 49;
else
values[n++] = c + 40;
} else if (gc->bg & COLOUR_FLAG_256) {
values[n++] = 48;
values[n++] = 5;
values[n++] = gc->bg & 0xff;
@@ -894,9 +948,19 @@ grid_string_cells_us(const struct grid_cell *gc, int *values)
{
size_t n;
u_char r, g, b;
int c;
n = 0;
if (gc->us & COLOUR_FLAG_256) {
if (gc->us & COLOUR_FLAG_THEME) {
c = colour_theme_terminal_colour(gc->us & 0xff);
if (c == 8)
values[n++] = 59;
else {
values[n++] = 58;
values[n++] = 5;
values[n++] = c;
}
} else if (gc->us & COLOUR_FLAG_256) {
values[n++] = 58;
values[n++] = 5;
values[n++] = gc->us & 0xff;
@@ -1223,6 +1287,8 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
sy++;
dy++;
}
grid_check_lines(dst);
}
/* Mark line as dead. */
@@ -1519,6 +1585,8 @@ grid_reflow(struct grid *gd, u_int sx)
gd->linedata = target->linedata;
free(target);
gd->scroll_generation++;
grid_check_lines(gd);
}
/* Convert to position based on wrapped lines. */

View File

@@ -41,6 +41,7 @@
*/
#define MAX_HYPERLINKS 5000
#define MAX_HYPERLINK_URI 1024
static long long hyperlinks_next_external_id = 1;
static u_int global_hyperlinks_count;
@@ -145,9 +146,13 @@ hyperlinks_put(struct hyperlinks *hl, const char *uri_in,
internal_id_in = "";
utf8_stravis(&uri, uri_in, VIS_OCTAL|VIS_CSTYLE);
utf8_stravis(&internal_id, internal_id_in, VIS_OCTAL|VIS_CSTYLE);
if (strlen(uri) > MAX_HYPERLINK_URI) {
free(uri);
return (0);
}
if (*internal_id_in != '\0') {
utf8_stravis(&internal_id, internal_id_in, VIS_OCTAL|VIS_CSTYLE);
if (*internal_id != '\0') {
find.uri = uri;
find.internal_id = internal_id;

View File

@@ -586,7 +586,7 @@ sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
len = 8192;
buf = xmalloc(len);
tmplen = xsnprintf(tmp, sizeof tmp, "\033P0;%uq", si->p2);
tmplen = xsnprintf(tmp, sizeof tmp, "\033P9;%uq", si->p2);
sixel_print_add(&buf, &len, &used, tmp, tmplen);
if (si->set_ra) {

14
input.c
View File

@@ -1953,16 +1953,14 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx)
case 2004:
screen_write_mode_clear(sctx, MODE_BRACKETPASTE);
break;
case 2026:
screen_write_stop_sync(ictx->wp);
break;
case 2031:
screen_write_mode_clear(sctx, MODE_THEME_UPDATES);
if (ictx->wp != NULL)
ictx->wp->flags &= ~PANE_THEMECHANGED;
break;
case 2026: /* synchronized output */
screen_write_stop_sync(ictx->wp);
if (ictx->wp != NULL)
ictx->wp->flags |= PANE_REDRAW;
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
break;
@@ -2065,7 +2063,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
ictx->wp->flags &= ~PANE_THEMECHANGED;
}
break;
case 2026: /* synchronized output */
case 2026:
screen_write_start_sync(ictx->wp);
break;
default:
@@ -2822,10 +2820,10 @@ input_exit_rename(struct input_ctx *ictx)
if (o != NULL)
options_remove_or_default(o, -1, NULL);
if (!options_get_number(w->options, "automatic-rename"))
window_set_name(w, "", WINDOW_NAME_FORBID_EXT);
window_set_name(w, "", 1);
} else {
options_set_number(w->options, "automatic-rename", 0);
window_set_name(w, ictx->input_buf, WINDOW_NAME_FORBID_EXT);
window_set_name(w, ictx->input_buf, 1);
}
server_redraw_window_borders(w);
server_status_window(w);

View File

@@ -405,6 +405,8 @@ key_bindings_init(void)
"bind -N 'Redraw the current client' r { refresh-client }",
"bind -N 'Choose a session from a list' s { choose-tree -Zs }",
"bind -N 'Show a clock' t { clock-mode }",
"bind -N 'Switch to a window' Tab { new-pane -E -x75% -y30% -X0 -Y0; move-pane -P bottom-centre; switch-mode -wk }",
"bind -N 'Switch to a session' BTab { new-pane -E -x75% -y30% -X0 -Y0; move-pane -P bottom-centre; switch-mode -sk }",
"bind -N 'Choose a window from a list' w { choose-tree -Zw }",
"bind -N 'Kill the active pane' x { confirm-before -p\"kill-pane #P? (y/n)\" kill-pane }",
"bind -N 'Zoom the active pane' z { resize-pane -Z }",
@@ -517,8 +519,8 @@ key_bindings_init(void)
"bind -Tcopy-mode M-l { send -X cursor-centre-horizontal }",
"bind -Tcopy-mode C-n { send -X cursor-down }",
"bind -Tcopy-mode C-p { send -X cursor-up }",
"bind -Tcopy-mode C-r { command-prompt -T search -ip'(search up)' -I'#{pane_search_string}' { send -X search-backward-incremental -- '%%' } }",
"bind -Tcopy-mode C-s { command-prompt -T search -ip'(search down)' -I'#{pane_search_string}' { send -X search-forward-incremental -- '%%' } }",
"bind -Tcopy-mode C-r { command-prompt -P -T search -ip'(search up)' -I'#{pane_search_string}' { send -X search-backward-incremental -- '%%' } }",
"bind -Tcopy-mode C-s { command-prompt -P -T search -ip'(search down)' -I'#{pane_search_string}' { send -X search-forward-incremental -- '%%' } }",
"bind -Tcopy-mode C-v { send -X page-down }",
"bind -Tcopy-mode C-w { send -X copy-pipe-and-cancel }",
"bind -Tcopy-mode Escape { send -X cancel }",
@@ -526,18 +528,18 @@ key_bindings_init(void)
"bind -Tcopy-mode Space { send -X page-down }",
"bind -Tcopy-mode , { send -X jump-reverse }",
"bind -Tcopy-mode \\; { send -X jump-again }",
"bind -Tcopy-mode F { command-prompt -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
"bind -Tcopy-mode F { command-prompt -P -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
"bind -Tcopy-mode N { send -X search-reverse }",
"bind -Tcopy-mode P { send -X toggle-position }",
"bind -Tcopy-mode R { send -X rectangle-toggle }",
"bind -Tcopy-mode T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
"bind -Tcopy-mode T { command-prompt -P -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
"bind -Tcopy-mode X { send -X set-mark }",
"bind -Tcopy-mode f { command-prompt -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
"bind -Tcopy-mode g { command-prompt -p'(goto line)' { send -X goto-line -- '%%' } }",
"bind -Tcopy-mode f { command-prompt -P -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
"bind -Tcopy-mode g { command-prompt -P -p'(goto line)' { send -X goto-line -- '%%' } }",
"bind -Tcopy-mode n { send -X search-again }",
"bind -Tcopy-mode q { send -X cancel }",
"bind -Tcopy-mode r { send -X refresh-toggle }",
"bind -Tcopy-mode t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
"bind -Tcopy-mode t { command-prompt -P -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
"bind -Tcopy-mode Home { send -X start-of-line }",
"bind -Tcopy-mode End { send -X end-of-line }",
"bind -Tcopy-mode MouseDown1Pane select-pane",
@@ -553,15 +555,15 @@ key_bindings_init(void)
"bind -Tcopy-mode Down { send -X cursor-down }",
"bind -Tcopy-mode Left { send -X cursor-left }",
"bind -Tcopy-mode Right { send -X cursor-right }",
"bind -Tcopy-mode M-1 { command-prompt -Np'(repeat)' -I1 { send -N '%%' } }",
"bind -Tcopy-mode M-2 { command-prompt -Np'(repeat)' -I2 { send -N '%%' } }",
"bind -Tcopy-mode M-3 { command-prompt -Np'(repeat)' -I3 { send -N '%%' } }",
"bind -Tcopy-mode M-4 { command-prompt -Np'(repeat)' -I4 { send -N '%%' } }",
"bind -Tcopy-mode M-5 { command-prompt -Np'(repeat)' -I5 { send -N '%%' } }",
"bind -Tcopy-mode M-6 { command-prompt -Np'(repeat)' -I6 { send -N '%%' } }",
"bind -Tcopy-mode M-7 { command-prompt -Np'(repeat)' -I7 { send -N '%%' } }",
"bind -Tcopy-mode M-8 { command-prompt -Np'(repeat)' -I8 { send -N '%%' } }",
"bind -Tcopy-mode M-9 { command-prompt -Np'(repeat)' -I9 { send -N '%%' } }",
"bind -Tcopy-mode M-1 { command-prompt -P -Np'(repeat)' -I1 { send -N '%%' } }",
"bind -Tcopy-mode M-2 { command-prompt -P -Np'(repeat)' -I2 { send -N '%%' } }",
"bind -Tcopy-mode M-3 { command-prompt -P -Np'(repeat)' -I3 { send -N '%%' } }",
"bind -Tcopy-mode M-4 { command-prompt -P -Np'(repeat)' -I4 { send -N '%%' } }",
"bind -Tcopy-mode M-5 { command-prompt -P -Np'(repeat)' -I5 { send -N '%%' } }",
"bind -Tcopy-mode M-6 { command-prompt -P -Np'(repeat)' -I6 { send -N '%%' } }",
"bind -Tcopy-mode M-7 { command-prompt -P -Np'(repeat)' -I7 { send -N '%%' } }",
"bind -Tcopy-mode M-8 { command-prompt -P -Np'(repeat)' -I8 { send -N '%%' } }",
"bind -Tcopy-mode M-9 { command-prompt -P -Np'(repeat)' -I9 { send -N '%%' } }",
"bind -Tcopy-mode M-< { send -X history-top }",
"bind -Tcopy-mode M-> { send -X history-bottom }",
"bind -Tcopy-mode M-R { send -X top-line }",
@@ -602,25 +604,25 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi Space { send -X begin-selection }",
"bind -Tcopy-mode-vi '$' { send -X end-of-line }",
"bind -Tcopy-mode-vi , { send -X jump-reverse }",
"bind -Tcopy-mode-vi / { command-prompt -T search -p'(search down)' { send -X search-forward -- '%%' } }",
"bind -Tcopy-mode-vi / { command-prompt -P -T search -p'(search down)' { send -X search-forward -- '%%' } }",
"bind -Tcopy-mode-vi 0 { send -X start-of-line }",
"bind -Tcopy-mode-vi 1 { command-prompt -Np'(repeat)' -I1 { send -N '%%' } }",
"bind -Tcopy-mode-vi 2 { command-prompt -Np'(repeat)' -I2 { send -N '%%' } }",
"bind -Tcopy-mode-vi 3 { command-prompt -Np'(repeat)' -I3 { send -N '%%' } }",
"bind -Tcopy-mode-vi 4 { command-prompt -Np'(repeat)' -I4 { send -N '%%' } }",
"bind -Tcopy-mode-vi 5 { command-prompt -Np'(repeat)' -I5 { send -N '%%' } }",
"bind -Tcopy-mode-vi 6 { command-prompt -Np'(repeat)' -I6 { send -N '%%' } }",
"bind -Tcopy-mode-vi 7 { command-prompt -Np'(repeat)' -I7 { send -N '%%' } }",
"bind -Tcopy-mode-vi 8 { command-prompt -Np'(repeat)' -I8 { send -N '%%' } }",
"bind -Tcopy-mode-vi 9 { command-prompt -Np'(repeat)' -I9 { send -N '%%' } }",
"bind -Tcopy-mode-vi : { command-prompt -p'(goto line)' { send -X goto-line -- '%%' } }",
"bind -Tcopy-mode-vi 1 { command-prompt -P -Np'(repeat)' -I1 { send -N '%%' } }",
"bind -Tcopy-mode-vi 2 { command-prompt -P -Np'(repeat)' -I2 { send -N '%%' } }",
"bind -Tcopy-mode-vi 3 { command-prompt -P -Np'(repeat)' -I3 { send -N '%%' } }",
"bind -Tcopy-mode-vi 4 { command-prompt -P -Np'(repeat)' -I4 { send -N '%%' } }",
"bind -Tcopy-mode-vi 5 { command-prompt -P -Np'(repeat)' -I5 { send -N '%%' } }",
"bind -Tcopy-mode-vi 6 { command-prompt -P -Np'(repeat)' -I6 { send -N '%%' } }",
"bind -Tcopy-mode-vi 7 { command-prompt -P -Np'(repeat)' -I7 { send -N '%%' } }",
"bind -Tcopy-mode-vi 8 { command-prompt -P -Np'(repeat)' -I8 { send -N '%%' } }",
"bind -Tcopy-mode-vi 9 { command-prompt -P -Np'(repeat)' -I9 { send -N '%%' } }",
"bind -Tcopy-mode-vi : { command-prompt -P -p'(goto line)' { send -X goto-line -- '%%' } }",
"bind -Tcopy-mode-vi \\; { send -X jump-again }",
"bind -Tcopy-mode-vi ? { command-prompt -T search -p'(search up)' { send -X search-backward -- '%%' } }",
"bind -Tcopy-mode-vi ? { command-prompt -P -T search -p'(search up)' { send -X search-backward -- '%%' } }",
"bind -Tcopy-mode-vi A { send -X append-selection-and-cancel }",
"bind -Tcopy-mode-vi B { send -X previous-space }",
"bind -Tcopy-mode-vi D { send -X copy-pipe-end-of-line-and-cancel }",
"bind -Tcopy-mode-vi E { send -X next-space-end }",
"bind -Tcopy-mode-vi F { command-prompt -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
"bind -Tcopy-mode-vi F { command-prompt -P -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
"bind -Tcopy-mode-vi G { send -X history-bottom }",
"bind -Tcopy-mode-vi H { send -X top-line }",
"bind -Tcopy-mode-vi J { send -X scroll-down }",
@@ -629,14 +631,14 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi M { send -X middle-line }",
"bind -Tcopy-mode-vi N { send -X search-reverse }",
"bind -Tcopy-mode-vi P { send -X toggle-position }",
"bind -Tcopy-mode-vi T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
"bind -Tcopy-mode-vi T { command-prompt -P -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
"bind -Tcopy-mode-vi V { send -X select-line }",
"bind -Tcopy-mode-vi W { send -X next-space }",
"bind -Tcopy-mode-vi X { send -X set-mark }",
"bind -Tcopy-mode-vi ^ { send -X back-to-indentation }",
"bind -Tcopy-mode-vi b { send -X previous-word }",
"bind -Tcopy-mode-vi e { send -X next-word-end }",
"bind -Tcopy-mode-vi f { command-prompt -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
"bind -Tcopy-mode-vi f { command-prompt -P -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
"bind -Tcopy-mode-vi g { send -X history-top }",
"bind -Tcopy-mode-vi h { send -X cursor-left }",
"bind -Tcopy-mode-vi j { send -X cursor-down }",
@@ -647,7 +649,7 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi o { send -X other-end }",
"bind -Tcopy-mode-vi q { send -X cancel }",
"bind -Tcopy-mode-vi r { send -X refresh-toggle }",
"bind -Tcopy-mode-vi t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
"bind -Tcopy-mode-vi t { command-prompt -P -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
"bind -Tcopy-mode-vi v { send -X rectangle-toggle }",
"bind -Tcopy-mode-vi w { send -X next-word }",
"bind -Tcopy-mode-vi '{' { send -X previous-paragraph }",

380
layout.c
View File

@@ -20,6 +20,7 @@
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
@@ -443,18 +444,22 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
{
struct window_pane *wp;
struct layout_cell *lc;
int status, scrollbars, sb_pos, sb_w, sb_pad;
u_int sx, sy;
int status, sb_w, sb_pad;
int old_xoff, old_yoff, changed = 0;
u_int sx, sy, old_sx, old_sy;
status = window_get_pane_status(w);
scrollbars = options_get_number(w->options, "pane-scrollbars");
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
TAILQ_FOREACH(wp, &w->panes, entry) {
if ((lc = wp->layout_cell) == NULL || wp == skip ||
!window_pane_is_visible(wp))
continue;
old_xoff = wp->xoff;
old_yoff = wp->yoff;
old_sx = wp->sx;
old_sy = wp->sy;
wp->xoff = lc->xoff;
wp->yoff = lc->yoff;
sx = lc->sx;
@@ -467,15 +472,15 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
sy--;
}
if (window_pane_show_scrollbar(wp, scrollbars)) {
if (window_pane_scrollbar_reserve(wp)) {
sb_w = wp->scrollbar_style.width;
sb_pad = wp->scrollbar_style.pad;
if (sb_w < 1)
sb_w = 1;
if (sb_pad < 0)
sb_pad = 0;
if (sb_pos == PANE_SCROLLBARS_LEFT) {
if ((int)sx - sb_w < PANE_MINIMUM) {
if (w->sb_pos == PANE_SCROLLBARS_LEFT) {
if ((int)sx - sb_w - sb_pad < PANE_MINIMUM) {
wp->xoff = wp->xoff +
(int)sx - PANE_MINIMUM;
sx = PANE_MINIMUM;
@@ -492,7 +497,15 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
}
window_pane_resize(wp, sx, sy);
if (wp->xoff != old_xoff ||
wp->yoff != old_yoff ||
wp->sx != old_sx ||
wp->sy != old_sy)
changed = 1;
}
if (changed)
redraw_invalidate_scene(w);
}
/* Count the number of available cells in a layout. */
@@ -523,16 +536,15 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
struct layout_cell *lcchild;
struct style *sb_style = &w->active->scrollbar_style;
u_int available, minimum;
int status, scrollbars;
int status;
status = window_get_pane_status(w);
scrollbars = options_get_number(w->options, "pane-scrollbars");
if (lc->type == LAYOUT_WINDOWPANE) {
/* Space available in this cell only. */
if (type == LAYOUT_LEFTRIGHT) {
available = lc->sx;
if (scrollbars)
if (w->sb == PANE_SCROLLBARS_ALWAYS)
minimum = PANE_MINIMUM + sb_style->width +
sb_style->pad;
else
@@ -858,7 +870,7 @@ layout_resize_pane_to(struct window_pane *wp, enum layout_type type,
}
/* Resize a floating pane to an absolute size. */
void
int
layout_resize_floating_pane_to(struct window_pane *wp, enum layout_type type,
u_int size, char **cause)
{
@@ -866,7 +878,7 @@ layout_resize_floating_pane_to(struct window_pane *wp, enum layout_type type,
if (~lc->flags & LAYOUT_CELL_FLOATING) {
*cause = xstrdup("pane is not floating");
return;
return (-1);
}
if (window_pane_get_pane_lines(wp) != PANE_LINES_NONE &&
@@ -874,17 +886,24 @@ layout_resize_floating_pane_to(struct window_pane *wp, enum layout_type type,
size -= 2;
if (size < PANE_MINIMUM || size > PANE_MAXIMUM) {
*cause = xstrdup("size is too big or too small");
return;
return (-1);
}
if (type == LAYOUT_TOPBOTTOM)
if (type == LAYOUT_TOPBOTTOM) {
if (lc->sy == size)
return (0);
lc->sy = size;
else
} else {
if (lc->sx == size)
return (0);
lc->sx = size;
}
redraw_invalidate_scene(wp->window);
return (0);
}
/* Resize a floating pane relative to its current size. */
void
int
layout_resize_floating_pane(struct window_pane *wp, enum layout_type type,
int change, int opposite, char **cause)
{
@@ -893,14 +912,16 @@ layout_resize_floating_pane(struct window_pane *wp, enum layout_type type,
if (~lc->flags & LAYOUT_CELL_FLOATING) {
*cause = xstrdup("pane is not floating");
return;
return (-1);
}
if (change == 0)
return (0);
if (type == LAYOUT_TOPBOTTOM) {
size = lc->sy + change;
if (size < PANE_MINIMUM || size > PANE_MAXIMUM) {
*cause = xstrdup("change is too big or too small");
return;
return (-1);
}
lc->sy = size;
if (opposite)
@@ -909,12 +930,14 @@ layout_resize_floating_pane(struct window_pane *wp, enum layout_type type,
size = lc->sx + change;
if (size < PANE_MINIMUM || size > PANE_MAXIMUM) {
*cause = xstrdup("change is too big or too small");
return;
return (-1);
}
lc->sx = size;
if (opposite)
lc->xoff -= change;
}
redraw_invalidate_scene(wp->window);
return (0);
}
/* Resize a layout cell. */
@@ -1278,6 +1301,100 @@ layout_split_sizes(struct layout_cell *lc, int size, int before,
}
/*
* Replaces the provided layout cell with a new node of the specified type and
* inserts the cell into it. Used when creating new cells requires a different
* layout type, or when the root layout is a window pane.
*/
struct layout_cell *
layout_replace_with_node(struct window *w, struct layout_cell *lc,
enum layout_type type)
{
struct layout_cell *lcparent;
lcparent = layout_create_cell(lc->parent);
layout_make_node(lcparent, type);
layout_set_size(lcparent, lc->sx, lc->sy, lc->xoff, lc->yoff);
if (lc->parent == NULL)
w->layout_root = lcparent;
else
TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry);
/* Insert the old cell. */
lc->parent = lcparent;
TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
return (lcparent);
}
/* Checks if there is enough space for two new panes. */
int
layout_split_check_space(struct window_pane *wp, struct layout_cell *lc,
enum layout_type type)
{
struct style *sb_style = &wp->scrollbar_style;
u_int minimum, sx = lc->sx, sy = lc->sy;
int status;
if (lc->flags & LAYOUT_CELL_FLOATING)
fatalx("floating cells cannot be split");
status = window_get_pane_status(wp->window);
switch (type) {
case LAYOUT_LEFTRIGHT:
if (wp->window->sb == PANE_SCROLLBARS_ALWAYS) {
minimum = PANE_MINIMUM * 2 + sb_style->width +
sb_style->pad;
} else
minimum = PANE_MINIMUM * 2 + 1;
if (sx < minimum)
return (0);
break;
case LAYOUT_TOPBOTTOM:
if (layout_add_horizontal_border(wp->window, lc, status))
minimum = PANE_MINIMUM * 2 + 2;
else
minimum = PANE_MINIMUM * 2 + 1;
if (sy < minimum)
return (0);
break;
default:
fatalx("bad layout type");
}
return (1);
}
/* Calculates the new cell sizes when splitting a pane. */
void
layout_split_sizes(struct layout_cell *lc, int size, int before,
enum layout_type type, u_int *size1, u_int *size2, u_int *saved_size)
{
u_int s1, s2, ss;
u_int sx = lc->sx, sy = lc->sy;
if (type == LAYOUT_LEFTRIGHT)
ss = sx;
else
ss = sy;
if (size < 0)
s2 = ((ss + 1) / 2) - 1;
else if (before)
s2 = ss - size - 1;
else
s2 = size;
if (s2 < PANE_MINIMUM)
s2 = PANE_MINIMUM;
else if (s2 > sx - 2)
s2 = ss - 2;
s1 = ss - 1 - s2;
*size1 = s1;
*size2 = s2;
*saved_size = ss;
}
/*
* Split a pane into two. size is a hint, or -1 for default half/half
* split. This must be followed by layout_assign_pane before much else happens!
@@ -1373,17 +1490,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
*/
/* Create and insert the replacement parent. */
lcparent = layout_create_cell(lc->parent);
layout_make_node(lcparent, type);
layout_set_size(lcparent, sx, sy, xoff, yoff);
if (lc->parent == NULL)
wp->window->layout_root = lcparent;
else
TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry);
/* Insert the old cell. */
lc->parent = lcparent;
TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
lcparent = layout_replace_with_node(wp->window, lc, type);
/* Create the new child cell. */
lcnew = layout_create_cell(lcparent);
@@ -1442,14 +1549,7 @@ layout_floating_pane(struct window *w, struct window_pane *wp, u_int sx,
* Adding a pane to a root that isn't node. Must create and
* insert a new root.
*/
lcparent = layout_create_cell(NULL);
layout_make_node(lcparent, LAYOUT_TOPBOTTOM);
layout_set_size(lcparent, w->sx, w->sy, 0, 0);
w->layout_root = lcparent;
/* Insert the old cell. */
lc->parent = lcparent;
TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
lcparent = layout_replace_with_node(w, lc, LAYOUT_TOPBOTTOM);
}
lcnew = layout_create_cell(lcparent);
@@ -1574,6 +1674,7 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
enum layout_type type;
u_int curval;
int size = -1;
char *error = NULL;
if (window_pane_is_floating(wp)) {
*cause = xstrdup("can't split a floating pane");
@@ -1600,15 +1701,16 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
if (args_has(args, 'l')) {
size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
item, cause);
item, &error);
} else if (args_has(args, 'p')) {
size = args_strtonum_and_expand(args, 'p', 0, 100, item,
cause);
if (*cause == NULL)
&error);
if (error == NULL)
size = curval * size / 100;
}
if (*cause != NULL) {
*cause = xstrdup("invalid tiled geometry");
if (error != NULL) {
xasprintf(cause, "invalid tiled geometry %s", error);
free(error);
return (NULL);
}
@@ -1625,11 +1727,30 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
return (lc);
}
void
layout_cell_floating_args_parse(struct cmdq_item *item, struct args *args,
struct window *w, u_int *sxp, u_int *syp, int *oxp, int *oyp, char **cause)
struct layout_cell *
layout_get_floating_cell(struct cmdq_item *item, struct args *args,
enum pane_lines lines, struct window *w, struct window_pane *wp,
char **cause)
{
int sx, sy, ox, oy;
struct layout_cell *lcnew;
u_int sx = UINT_MAX, sy = UINT_MAX;
int ox = INT_MAX, oy = INT_MAX;
if (layout_floating_args_parse(item, args, lines, w, &sx, &sy, &ox, &oy,
cause) != 0)
return (NULL);
lcnew = layout_floating_pane(w, wp, sx, sy, ox, oy);
return (lcnew);
}
int
layout_floating_args_parse(struct cmdq_item *item, struct args *args,
enum pane_lines lines, struct window *w, u_int *sxp, u_int *syp, int *oxp,
int *oyp, char **cause)
{
int sx, sy, ox, oy;
char *error = NULL;
sx = *sxp == UINT_MAX ? w->sx / 2 : *sxp;
sy = *syp == UINT_MAX ? w->sy / 4 : *syp;
@@ -1637,28 +1758,44 @@ layout_cell_floating_args_parse(struct cmdq_item *item, struct args *args,
oy = *oyp == INT_MAX ? INT_MAX : *oyp;
if (args_has(args, 'x')) {
sx = args_percentage_and_expand(args, 'x', 0, w->sx - 1, w->sx,
item, cause);
if (*cause != NULL)
return;
sx = args_percentage_and_expand(args, 'x', 0, PANE_MAXIMUM,
w->sx, item, &error);
if (error != NULL) {
xasprintf(cause, "position %s", error);
free(error);
return (-1);
}
if (lines != PANE_LINES_NONE)
sx -= 2;
}
if (args_has(args, 'y')) {
sy = args_percentage_and_expand(args, 'y', 0, w->sy - 1, w->sy,
item, cause);
if (*cause != NULL)
return;
sy = args_percentage_and_expand(args, 'y', 0, PANE_MAXIMUM,
w->sy, item, &error);
if (error != NULL) {
xasprintf(cause, "position %s", error);
free(error);
return (-1);
}
if (lines != PANE_LINES_NONE)
sy -= 2;
}
if (args_has(args, 'X')) {
ox = args_percentage_and_expand(args, 'X', -(int)w->sx, w->sx,
w->sx, item, cause);
if (*cause != NULL)
return;
ox = args_percentage_and_expand(args, 'X', -sx, w->sx,
w->sx, item, &error);
if (error != NULL) {
xasprintf(cause, "position %s", error);
free(error);
return (-1);
}
}
if (args_has(args, 'Y')) {
oy = args_percentage_and_expand(args, 'Y', -(int)w->sy, w->sy,
w->sy, item, cause);
if (*cause != NULL)
return;
oy = args_percentage_and_expand(args, 'Y', -sy, w->sy,
w->sy, item, &error);
if (error != NULL) {
xasprintf(cause, "position %s", error);
free(error);
return (-1);
}
}
if (ox == INT_MAX) {
@@ -1670,7 +1807,9 @@ layout_cell_floating_args_parse(struct cmdq_item *item, struct args *args,
ox = 4;
}
w->last_new_pane_x = ox;
}
} else
if (lines != PANE_LINES_NONE)
ox += 1;
if (oy == INT_MAX) {
if (w->last_new_pane_y == 0)
oy = 2;
@@ -1680,39 +1819,24 @@ layout_cell_floating_args_parse(struct cmdq_item *item, struct args *args,
oy = 2;
}
w->last_new_pane_y = oy;
} else
if (lines != PANE_LINES_NONE)
oy += 1;
if (sx < PANE_MINIMUM || sx > PANE_MAXIMUM) {
*cause = xstrdup("invalid width");
return (-1);
}
if (sy < PANE_MINIMUM || sy > PANE_MAXIMUM) {
*cause = xstrdup("invalid height");
return (-1);
}
*sxp = sx;
*syp = sy;
*oxp = ox;
*oyp = oy;
}
/* Get a new floating cell. */
struct layout_cell *
layout_get_floating_cell(struct cmdq_item *item, struct args *args,
struct window *w, struct window_pane *wp, char **cause)
{
struct layout_cell *lcnew;
u_int sx = UINT_MAX, sy = UINT_MAX;
int ox = INT_MAX, oy = INT_MAX;
layout_cell_floating_args_parse(item, args, w, &sx, &sy, &ox, &oy,
cause);
if (*cause != NULL) {
return (NULL);
}
if (sx < PANE_MINIMUM || sx > PANE_MAXIMUM) {
*cause = xstrdup("invalid width");
return (NULL);
}
if (sy < PANE_MINIMUM || sy > PANE_MAXIMUM) {
*cause = xstrdup("invalid height");
return (NULL);
}
lcnew = layout_floating_pane(w, wp, sx, sy, ox, oy);
return (lcnew);
return (0);
}
/*
@@ -1746,8 +1870,76 @@ layout_remove_tile(struct window *w, struct layout_cell *lc)
layout_resize_adjust(w, lcneighbour, type, change);
}
/* Zeroing out the cell geometry until the cell is retiled. */
layout_set_size(lc, 0, 0, 0, 0);
/*
* Zeroing out the cell geometry until the cell is retiled unless this
* is the top level node.
*/
if (lc->parent != NULL)
layout_set_size(lc, 0, 0, 0, 0);
return (1);
}
/*
* Inserts a cell back into the tiled layout by taking half the space from its
* nearest neighbour.
*/
int
layout_insert_tile(struct window *w, struct layout_cell *lc)
{
struct layout_cell *lcneighbour, *lctiled, *lcparent;
enum layout_type type;
u_int size1, size2, saved_size;
if (lc == NULL)
fatalx("layout cell cannot be null when tiling");
lcparent = lc->parent;
if (lc->flags & LAYOUT_CELL_FLOATING)
return (1);
if (lcparent == NULL) {
/* Only pane in the layout. */
layout_set_size(lc, w->sx, w->sy, 0, 0);
return (1);
}
type = lcparent->type;
lcneighbour = layout_cell_get_neighbour(lc);
if (lcneighbour == NULL) {
/*
* This will become the only visible cell in the parent.
* Tile the parent, then set the child's 'split' size.
*/
layout_insert_tile(w, lcparent);
if (type == LAYOUT_LEFTRIGHT)
size1 = lcparent->sx;
else
size1 = lcparent->sy;
layout_resize_set_size(w, lc, type, size1);
} else {
/*
* If the neighbour is a node, a tiled child in the subtree of
* the neighbour is needed to check for space.
*/
lctiled = layout_cell_get_first_tiled(lcneighbour);
if (!layout_split_check_space(lctiled->wp, lcneighbour, type))
return (0);
layout_split_sizes(lcneighbour, -1, 0, type, &size1, &size2,
&saved_size);
layout_resize_set_size(w, lc, type, size1);
layout_resize_set_size(w, lcneighbour, type, size2);
}
/* Setting opposite of the 'split' size to that of the parent. */
if (lcparent->type == LAYOUT_LEFTRIGHT) {
size1 = lcparent->sy;
type = LAYOUT_TOPBOTTOM;
} else {
size1 = lcparent->sx;
type = LAYOUT_LEFTRIGHT;
}
layout_resize_set_size(w, lc, type, size1);
return (1);
}

7
menu.c
View File

@@ -257,8 +257,7 @@ menu_reapply_styles(struct menu_data *md, struct client *c)
}
void
menu_draw_cb(struct client *c, void *data,
__unused struct screen_redraw_ctx *rctx)
menu_draw_cb(struct client *c, void *data)
{
struct menu_data *md = data;
struct tty *tty = &c->tty;
@@ -636,7 +635,7 @@ menu_display(struct menu *menu, int flags, int starting_choice,
style, selected_style, border_style, fs, cb, data);
if (md == NULL)
return (-1);
server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb,
menu_key_cb, menu_free_cb, menu_resize_cb, md);
server_client_set_overlay(c, 0, menu_check_cb, menu_mode_cb,
menu_draw_cb, menu_key_cb, menu_free_cb, menu_resize_cb, md);
return (0);
}

View File

@@ -36,7 +36,26 @@ enum mode_tree_preview {
MODE_TREE_PREVIEW_BIG
};
#define MODE_TREE_PREFIX_STYLE \
"#{?mode_tree_selected,#[default]#[noacs]," \
"#[fg=themelightgrey]#[bg=default]#[noacs]}"
#define MODE_TREE_PREFIX_FORMAT \
MODE_TREE_PREFIX_STYLE \
"#{p/#{mode_tree_key_width}:" \
"#{?#{!=:#{mode_tree_key},},(#{mode_tree_key}),}}" \
"#{R:#{?mode_tree_parent_last, ," \
"#[acs]x" MODE_TREE_PREFIX_STYLE " }," \
"#{mode_tree_repeat}}" \
"#{?mode_tree_branch," \
"#[acs]#{?mode_tree_last,mq,tq}" MODE_TREE_PREFIX_STYLE "> ,}" \
"#{?mode_tree_has_children," \
"#{?mode_tree_expanded,#[fg=themered]-" MODE_TREE_PREFIX_STYLE " ," \
"#[fg=themegreen]+" MODE_TREE_PREFIX_STYLE " }," \
"#{?mode_tree_flat,, }}"
struct mode_tree_item;
struct mode_tree_prompt;
TAILQ_HEAD(mode_tree_list, mode_tree_item);
struct mode_tree_data {
@@ -51,11 +70,11 @@ struct mode_tree_data {
struct sort_criteria sort_crit;
const char *view_name;
mode_tree_build_cb buildcb;
mode_tree_draw_cb drawcb;
mode_tree_search_cb searchcb;
mode_tree_menu_cb menucb;
mode_tree_height_cb heightcb;
mode_tree_build_cb buildcb;
mode_tree_draw_cb drawcb;
mode_tree_search_cb searchcb;
mode_tree_menu_cb menucb;
mode_tree_height_cb heightcb;
mode_tree_key_cb keycb;
mode_tree_swap_cb swapcb;
mode_tree_sort_cb sortcb;
@@ -77,6 +96,10 @@ struct mode_tree_data {
u_int current;
struct screen screen;
struct prompt *prompt;
struct mode_tree_prompt *prompt_data;
u_int prompt_cx;
int prompt_top;
int preview;
char *search;
@@ -124,9 +147,25 @@ struct mode_tree_menu {
u_int line;
};
static void mode_tree_free_items(struct mode_tree_list *);
static void mode_tree_draw_help(struct mode_tree_data *,
struct screen_write_ctx *);
/*
* Wrapper around a prompt owned by a mode tree. The mode tree holds a reference
* while the prompt is alive; the wrapper callbacks forward to the caller's
* callbacks and drop that reference when the prompt is freed.
*/
struct mode_tree_prompt {
struct mode_tree_data *mtd;
struct client *c;
mode_tree_prompt_input_cb inputcb;
prompt_free_cb freecb;
void *data;
};
static void mode_tree_free_items(struct mode_tree_list *);
static void mode_tree_draw_help(struct mode_tree_data *,
struct screen_write_ctx *);
static void mode_tree_draw_prompt(struct mode_tree_data *,
struct screen_write_ctx *);
static enum cmd_retval mode_tree_prompt_accept(struct cmdq_item *, void *);
static const struct menu_item mode_tree_menu_items[] = {
{ "Scroll Left", '<', NULL },
@@ -138,31 +177,53 @@ static const struct menu_item mode_tree_menu_items[] = {
};
static const char* mode_tree_help_start[] = {
"#[bold] Up, k #[default]#[acs]x#[default] Move cursor up",
"#[bold] Down, j #[default]#[acs]x#[default] Move cursor down",
"#[bold] g #[default]#[acs]x#[default] Go to top",
"#[bold] G #[default]#[acs]x#[default] Go to bottom",
"#[bold] PPage, C-b #[default]#[acs]x#[default] Page up",
"#[bold] NPage, C-f #[default]#[acs]x#[default] Page down",
"#[bold] Left, h #[default]#[acs]x#[default] Collapse %1",
"#[bold] Right, l #[default]#[acs]x#[default] Expand %1",
"#[bold] M-- #[default]#[acs]x#[default] Collapse all %1s",
"#[bold] M-+ #[default]#[acs]x#[default] Expand all %1s",
"#[bold] t #[default]#[acs]x#[default] Toggle %1 tag",
"#[bold] T #[default]#[acs]x#[default] Untag all %1s",
"#[bold] C-t #[default]#[acs]x#[default] Tag all %1s",
"#[bold] C-s #[default]#[acs]x#[default] Search forward",
"#[bold] C-r #[default]#[acs]x#[default] Search backward",
"#[bold] n #[default]#[acs]x#[default] Repeat search forward",
"#[bold] N #[default]#[acs]x#[default] Repeat search backward",
"#[bold] f #[default]#[acs]x#[default] Filter %1s",
"#[bold] O #[default]#[acs]x#[default] Change sort order",
"#[bold] r #[default]#[acs]x#[default] Reverse sort order",
"#[bold] v #[default]#[acs]x#[default] Toggle preview",
"#[fg=themelightgrey]"
" Up, k #[#{E:tree-mode-border-style},acs]x#[default] Move cursor up",
"#[fg=themelightgrey]"
" Down, j #[#{E:tree-mode-border-style},acs]x#[default] Move cursor down",
"#[fg=themelightgrey]"
" g #[#{E:tree-mode-border-style},acs]x#[default] Go to top",
"#[fg=themelightgrey]"
" G #[#{E:tree-mode-border-style},acs]x#[default] Go to bottom",
"#[fg=themelightgrey]"
" PPage, C-b #[#{E:tree-mode-border-style},acs]x#[default] Page up",
"#[fg=themelightgrey]"
" NPage, C-f #[#{E:tree-mode-border-style},acs]x#[default] Page down",
"#[fg=themelightgrey]"
" Left, h #[#{E:tree-mode-border-style},acs]x#[default] Collapse %1",
"#[fg=themelightgrey]"
" Right, l #[#{E:tree-mode-border-style},acs]x#[default] Expand %1",
"#[fg=themelightgrey]"
" M-- #[#{E:tree-mode-border-style},acs]x#[default] Collapse all %1s",
"#[fg=themelightgrey]"
" M-+ #[#{E:tree-mode-border-style},acs]x#[default] Expand all %1s",
"#[fg=themelightgrey]"
" t #[#{E:tree-mode-border-style},acs]x#[default] Toggle %1 tag",
"#[fg=themelightgrey]"
" T #[#{E:tree-mode-border-style},acs]x#[default] Untag all %1s",
"#[fg=themelightgrey]"
" C-t #[#{E:tree-mode-border-style},acs]x#[default] Tag all %1s",
"#[fg=themelightgrey]"
" C-s #[#{E:tree-mode-border-style},acs]x#[default] Search forward",
"#[fg=themelightgrey]"
" C-r #[#{E:tree-mode-border-style},acs]x#[default] Search backward",
"#[fg=themelightgrey]"
" n #[#{E:tree-mode-border-style},acs]x#[default] Repeat search forward",
"#[fg=themelightgrey]"
" N #[#{E:tree-mode-border-style},acs]x#[default] Repeat search backward",
"#[fg=themelightgrey]"
" f #[#{E:tree-mode-border-style},acs]x#[default] Filter %1s",
"#[fg=themelightgrey]"
" O #[#{E:tree-mode-border-style},acs]x#[default] Change sort order",
"#[fg=themelightgrey]"
" r #[#{E:tree-mode-border-style},acs]x#[default] Reverse sort order",
"#[fg=themelightgrey]"
" v #[#{E:tree-mode-border-style},acs]x#[default] Toggle preview",
NULL
};
static const char* mode_tree_help_end[] = {
"#[bold] q, Escape #[default]#[acs]x#[default] Exit mode",
"#[fg=themelightgrey]"
" q, Escape #[#{E:tree-mode-border-style},acs]x#[default] Exit mode",
NULL
};
#define MODE_TREE_HELP_DEFAULT_WIDTH 39
@@ -382,6 +443,13 @@ mode_tree_get_current_name(struct mode_tree_data *mtd)
return (mtd->line_list[mtd->current].item->name);
}
void
mode_tree_select_top(struct mode_tree_data *mtd)
{
mtd->current = 0;
mtd->offset = 0;
}
void
mode_tree_expand_current(struct mode_tree_data *mtd)
{
@@ -509,7 +577,9 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mtd->modedata = modedata;
mtd->menu = menu;
if (args_has(args, 'N') > 1)
if (drawcb == NULL)
mtd->preview = MODE_TREE_PREVIEW_OFF;
else if (args_has(args, 'N') > 1)
mtd->preview = MODE_TREE_PREVIEW_BIG;
else if (args_has(args, 'N'))
mtd->preview = MODE_TREE_PREVIEW_OFF;
@@ -641,6 +711,7 @@ mode_tree_free(struct mode_tree_data *mtd)
if (mtd->zoomed == 0)
server_unzoom_window(wp->window);
mode_tree_clear_prompt(mtd);
mode_tree_free_items(&mtd->children);
mode_tree_clear_lines(mtd);
screen_free(&mtd->screen);
@@ -723,8 +794,8 @@ mode_tree_no_tag(struct mode_tree_item *mti)
}
/*
* Set the alignment of mti->name: -1 to align left, 0 (default) to not align,
* or 1 to align right.
* Set the alignment of the item name: -1 to align left, 0 (default) to not
* align, or 1 to align right.
*/
void
mode_tree_align(struct mode_tree_item *mti, int align)
@@ -753,25 +824,34 @@ mode_tree_draw(struct mode_tree_data *mtd)
struct mode_tree_item *mti;
struct options *oo = wp->window->options;
struct screen_write_ctx ctx;
struct grid_cell gc0, gc;
u_int w, h, i, j, sy, box_x, box_y, width;
char *text, *start, *key;
const char *tag, *symbol;
size_t size, n;
int keylen, pad, alignlen[mtd->maxdepth + 1];
struct format_tree *ft;
struct grid_cell gc0, gc, box_gc;
u_int w, h, i, sy, box_x, box_y;
u_int width, text_width, prefix_width, left;
char *text, *prefix;
const char *tag, *separator;
size_t n;
int keylen, alignlen[mtd->maxdepth + 1];
int dfg, dfg0;
if (mtd->line_size == 0)
return;
memcpy(&gc0, &grid_default_cell, sizeof gc0);
memcpy(&gc, &grid_default_cell, sizeof gc);
style_apply(&gc, oo, "mode-style", NULL);
style_apply(&gc, oo, "tree-mode-selection-style", NULL);
memcpy(&box_gc, &grid_default_cell, sizeof box_gc);
style_apply(&box_gc, oo, "tree-mode-border-style", NULL);
dfg = gc.fg;
dfg0 = gc0.fg;
w = mtd->width;
h = mtd->height;
screen_write_start(&ctx, s);
screen_write_clearscreen(&ctx, 8);
ft = format_create_defaults(NULL, NULL, NULL, NULL, wp);
keylen = 0;
for (i = 0; i < mtd->line_size; i++) {
@@ -802,81 +882,98 @@ mode_tree_draw(struct mode_tree_data *mtd)
screen_write_cursormove(&ctx, 0, i - mtd->offset, 0);
pad = keylen - 2 - mti->keylen;
if (mti->key != KEYC_NONE)
xasprintf(&key, "(%s)%*s", mti->keystr, pad, "");
format_add(ft, "mode_tree_key", "%s", mti->keystr);
else
key = xstrdup("");
if (line->flat)
symbol = "";
else if (TAILQ_EMPTY(&mti->children))
symbol = " ";
else if (mti->expanded)
symbol = "- ";
else
symbol = "+ ";
if (line->depth == 0)
start = xstrdup(symbol);
else {
size = (4 * line->depth) + 32;
start = xcalloc(1, size);
for (j = 1; j < line->depth; j++) {
if (mti->parent != NULL &&
mtd->line_list[mti->parent->line].last)
strlcat(start, " ", size);
else
strlcat(start, "\001x\001 ", size);
}
if (line->last)
strlcat(start, "\001mq\001> ", size);
format_add(ft, "mode_tree_key", "%s", "");
format_add(ft, "mode_tree_key_width", "%d", keylen);
format_add(ft, "mode_tree_selected", "%d", i == mtd->current);
if (line->depth == 0) {
format_add(ft, "mode_tree_repeat", "%u", 0);
format_add(ft, "mode_tree_branch", "0");
format_add(ft, "mode_tree_parent_last", "0");
} else {
format_add(ft, "mode_tree_repeat", "%u",
line->depth - 1);
format_add(ft, "mode_tree_branch", "1");
if (mti->parent != NULL &&
mtd->line_list[mti->parent->line].last)
format_add(ft, "mode_tree_parent_last", "1");
else
strlcat(start, "\001tq\001> ", size);
strlcat(start, symbol, size);
format_add(ft, "mode_tree_parent_last", "0");
}
if (TAILQ_EMPTY(&mti->children))
format_add(ft, "mode_tree_has_children", "0");
else
format_add(ft, "mode_tree_has_children", "1");
format_add(ft, "mode_tree_last", "%d", line->last);
format_add(ft, "mode_tree_expanded", "%d", mti->expanded);
format_add(ft, "mode_tree_flat", "%d", line->flat);
prefix = format_expand(ft, MODE_TREE_PREFIX_FORMAT);
prefix_width = format_width(prefix);
if (prefix_width > w)
prefix_width = w;
if (mti->tagged)
tag = "*";
else
tag = "";
xasprintf(&text, "%-*s%s%*s%s%s", keylen, key, start,
mti->align * alignlen[line->depth], mti->name, tag,
(mti->text != NULL) ? ": " : "" );
width = utf8_cstrwidth(text);
if (width > w)
width = w;
free(start);
if (mti->text != NULL)
separator = "#[fg=themelightgrey]: #[default]";
else
separator = "";
xasprintf(&text, "%*s%s%s",
mti->align * alignlen[line->depth], mti->name, tag, separator);
text_width = format_width(text);
left = (prefix_width < w) ? (w - prefix_width) : 0;
if (text_width > left)
text_width = left;
width = prefix_width + text_width;
if (mti->tagged) {
gc.attr ^= GRID_ATTR_BRIGHT;
gc0.attr ^= GRID_ATTR_BRIGHT;
gc.fg = COLOUR_THEME_CYAN|COLOUR_FLAG_THEME;
gc0.fg = COLOUR_THEME_CYAN|COLOUR_FLAG_THEME;
}
if (i != mtd->current) {
screen_write_clearendofline(&ctx, 8);
screen_write_nputs(&ctx, w, &gc0, "%s", text);
if (mti->text != NULL) {
format_draw(&ctx, &gc0, w - width, mti->text,
NULL, 0);
format_draw(&ctx, &grid_default_cell, prefix_width,
prefix, NULL, 0);
if (left != 0) {
screen_write_cursormove(&ctx, prefix_width,
i - mtd->offset, 0);
format_draw(&ctx, &gc0, left, text, NULL, 0);
if (mti->text != NULL && width < w) {
screen_write_cursormove(&ctx, width,
i - mtd->offset, 0);
format_draw(&ctx, &gc0, w - width,
mti->text, NULL, 0);
}
}
} else {
screen_write_clearendofline(&ctx, gc.bg);
screen_write_nputs(&ctx, w, &gc, "%s", text);
if (mti->text != NULL) {
format_draw(&ctx, &gc, w - width, mti->text,
NULL, 0);
format_draw(&ctx, &gc, prefix_width, prefix, NULL, 0);
if (left != 0) {
screen_write_cursormove(&ctx, prefix_width,
i - mtd->offset, 0);
format_draw(&ctx, &gc, left, text, NULL, 0);
if (mti->text != NULL && width < w) {
screen_write_cursormove(&ctx, width,
i - mtd->offset, 0);
format_draw(&ctx, &gc, w - width,
mti->text, NULL, 0);
}
}
}
free(text);
free(key);
free(prefix);
if (mti->tagged) {
gc.attr ^= GRID_ATTR_BRIGHT;
gc0.attr ^= GRID_ATTR_BRIGHT;
gc.fg = dfg;
gc0.fg = dfg0;
}
}
format_free(ft);
if (mtd->preview == MODE_TREE_PREVIEW_OFF)
goto done;
@@ -891,7 +988,7 @@ mode_tree_draw(struct mode_tree_data *mtd)
mti = mti->parent;
screen_write_cursormove(&ctx, 0, h, 0);
screen_write_box(&ctx, w, sy - h, BOX_LINES_DEFAULT, NULL, NULL);
screen_write_box(&ctx, w, sy - h, BOX_LINES_DEFAULT, &box_gc, NULL);
if (mtd->sort_crit.order_seq != NULL) {
xasprintf(&text, " %s (sort: %s%s)%s%s%s", mti->name,
@@ -904,21 +1001,21 @@ mode_tree_draw(struct mode_tree_data *mtd)
xasprintf(&text, " %s", mti->name);
if (w - 2 >= strlen(text)) {
screen_write_cursormove(&ctx, 1, h, 0);
screen_write_puts(&ctx, &gc0, "%s", text);
screen_write_puts(&ctx, &box_gc, "%s", text);
if (mtd->no_matches)
n = (sizeof "no matches") - 1;
else
n = (sizeof "active") - 1;
if (mtd->filter != NULL && w - 2 >= strlen(text) + 10 + n + 2) {
screen_write_puts(&ctx, &gc0, " (filter: ");
screen_write_puts(&ctx, &box_gc, " (filter: ");
if (mtd->no_matches)
screen_write_puts(&ctx, &gc, "no matches");
screen_write_puts(&ctx, &box_gc, "no matches");
else
screen_write_puts(&ctx, &gc0, "active");
screen_write_puts(&ctx, &gc0, ") ");
screen_write_puts(&ctx, &box_gc, "active");
screen_write_puts(&ctx, &box_gc, ") ");
} else
screen_write_puts(&ctx, &gc0, " ");
screen_write_puts(&ctx, &box_gc, " ");
}
free(text);
@@ -933,10 +1030,149 @@ mode_tree_draw(struct mode_tree_data *mtd)
done:
if (mtd->help)
mode_tree_draw_help(mtd, &ctx);
screen_write_cursormove(&ctx, 0, mtd->current - mtd->offset, 0);
if (mtd->prompt != NULL)
mode_tree_draw_prompt(mtd, &ctx);
else {
s->mode &= ~MODE_CURSOR;
screen_write_cursormove(&ctx, 0, mtd->current - mtd->offset, 0);
}
screen_write_stop(&ctx);
}
static void
mode_tree_draw_prompt(struct mode_tree_data *mtd, struct screen_write_ctx *ctx)
{
struct screen *s = &mtd->screen;
struct prompt_draw_data pdd;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
u_int py;
if (sx == 0 || sy == 0)
return;
if (mtd->prompt_top)
py = 0;
else
py = sy - 1;
pdd.ctx = ctx;
pdd.cursor_x = &mtd->prompt_cx;
pdd.area_x = 0;
pdd.area_width = sx;
pdd.prompt_line = py;
s->mode |= MODE_CURSOR;
prompt_draw(mtd->prompt, &pdd);
screen_write_cursormove(ctx, mtd->prompt_cx, py, 0);
}
void
mode_tree_clear_prompt(struct mode_tree_data *mtd)
{
struct prompt *prompt = mtd->prompt;
if (mtd->prompt != NULL) {
mtd->prompt = NULL;
prompt_free(prompt);
mtd->screen.mode &= ~MODE_CURSOR;
}
}
int
mode_tree_has_prompt(struct mode_tree_data *mtd)
{
return (mtd->prompt != NULL);
}
static enum cmd_retval
mode_tree_prompt_accept(struct cmdq_item *item, void *data)
{
struct mode_tree_data *mtd = data;
struct client *c = cmdq_get_client(item);
key_code key = 'y';
if (mtd->prompt != NULL && c != NULL)
mode_tree_key(mtd, c, &key, NULL, NULL, NULL);
mode_tree_remove_ref(mtd);
return (CMD_RETURN_NORMAL);
}
static enum prompt_result
mode_tree_prompt_input_callback(void *data, const char *s,
enum prompt_key_result key)
{
struct mode_tree_prompt *mtp = data;
if (mtp->inputcb != NULL)
return (mtp->inputcb(mtp->c, mtp->data, s, key));
return (PROMPT_CLOSE);
}
static void
mode_tree_prompt_free_callback(void *data)
{
struct mode_tree_prompt *mtp = data;
if (mtp->mtd->prompt_data == mtp)
mtp->mtd->prompt_data = NULL;
if (mtp->freecb != NULL)
mtp->freecb(mtp->data);
mode_tree_remove_ref(mtp->mtd);
free(mtp);
}
void
mode_tree_set_prompt(struct mode_tree_data *mtd, struct client *c,
const char *prompt, const char *input, enum prompt_type type, int flags,
mode_tree_prompt_input_cb inputcb, prompt_free_cb freecb, void *data)
{
struct session *s;
struct options *oo;
struct prompt_create_data pd;
struct mode_tree_prompt *mtp;
if (c != NULL && c->session != NULL) {
s = c->session;
oo = s->options;
} else {
s = NULL;
oo = global_s_options;
}
mode_tree_clear_prompt(mtd);
mtp = xcalloc(1, sizeof *mtp);
mtp->mtd = mtd;
mtp->c = c;
mtp->inputcb = inputcb;
mtp->freecb = freecb;
mtp->data = data;
mtd->references++;
mtd->prompt_top = options_get_number(oo, "status-position") == 0;
memset(&pd, 0, sizeof pd);
prompt_set_options(&pd, s);
pd.prompt = prompt;
pd.input = input;
pd.type = type;
pd.flags = flags|PROMPT_ISMODE;
pd.inputcb = mode_tree_prompt_input_callback;
pd.freecb = mode_tree_prompt_free_callback;
pd.data = mtp;
mtd->prompt = prompt_create(&pd);
mtd->prompt_data = mtp;
mode_tree_draw(mtd);
mtd->wp->flags |= PANE_REDRAW;
if ((flags & PROMPT_SINGLE) && (flags & PROMPT_ACCEPT) && c != NULL) {
mtd->references++;
cmdq_append(c, cmdq_get_callback(mode_tree_prompt_accept, mtd));
}
}
static struct mode_tree_item *
mode_tree_search_backward(struct mode_tree_data *mtd)
{
@@ -1057,41 +1293,37 @@ mode_tree_search_set(struct mode_tree_data *mtd)
mtd->wp->flags |= PANE_REDRAW;
}
static int
static enum prompt_result
mode_tree_search_callback(__unused struct client *c, void *data, const char *s,
__unused int done)
enum prompt_key_result key)
{
struct mode_tree_data *mtd = data;
if (mtd->dead)
return (0);
return (PROMPT_CLOSE);
free(mtd->search);
if (s == NULL || *s == '\0') {
if (s == NULL || *s == '\0')
mtd->search = NULL;
return (0);
else {
mtd->search = xstrdup(s);
mtd->search_icase = mode_tree_is_lowercase(s);
mode_tree_search_set(mtd);
}
mtd->search = xstrdup(s);
mtd->search_icase = mode_tree_is_lowercase(s);
mode_tree_search_set(mtd);
return (0);
if (key == PROMPT_KEY_HANDLED)
return (PROMPT_CONTINUE);
return (PROMPT_CLOSE);
}
static void
mode_tree_search_free(void *data)
{
mode_tree_remove_ref(data);
}
static int
static enum prompt_result
mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
__unused int done)
enum prompt_key_result key)
{
struct mode_tree_data *mtd = data;
if (mtd->dead)
return (0);
return (PROMPT_CLOSE);
if (mtd->filter != NULL)
free(mtd->filter);
@@ -1104,13 +1336,20 @@ mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
mode_tree_draw(mtd);
mtd->wp->flags |= PANE_REDRAW;
return (0);
if (key == PROMPT_KEY_HANDLED)
return (PROMPT_CONTINUE);
return (PROMPT_CLOSE);
}
static void
mode_tree_filter_free(void *data)
mode_tree_clear_filter(struct mode_tree_data *mtd)
{
mode_tree_remove_ref(data);
free(mtd->filter);
mtd->filter = NULL;
mode_tree_build(mtd);
mode_tree_draw(mtd);
mtd->wp->flags |= PANE_REDRAW;
}
static void
@@ -1181,12 +1420,14 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
static void
mode_tree_draw_help_line(struct screen_write_ctx *ctx,
const struct grid_cell *gc, const char *line, const char *item, u_int x,
u_int y, u_int w)
const struct grid_cell *gc, struct format_tree *ft, const char *line,
const char *item, u_int x, u_int y, u_int w)
{
char *expanded;
char *expanded, *replaced;
expanded = cmd_template_replace(line, item, 1);
replaced = cmd_template_replace(line, item, 1);
expanded = format_expand(ft, replaced);
free(replaced);
screen_write_cursormove(ctx, x, y, 0);
screen_write_clearcharacter(ctx, w, gc->bg);
screen_write_cursormove(ctx, x, y, 0);
@@ -1198,10 +1439,12 @@ static void
mode_tree_draw_help(struct mode_tree_data *mtd, struct screen_write_ctx *ctx)
{
struct screen *s = &mtd->screen;
struct grid_cell gc;
struct options *oo = mtd->wp->window->options;
struct grid_cell box_gc, gc;
struct format_tree *ft;
const char **line, **lines = NULL, *item = "item";
u_int sx = screen_size_x(s), sy = screen_size_y(s);
u_int x, y, w, h = 0, box_w, box_h;
u_int x, y, w, h = 0, box_w, box_h;
if (mtd->helpcb == NULL)
w = MODE_TREE_HELP_DEFAULT_WIDTH;
@@ -1224,18 +1467,22 @@ mode_tree_draw_help(struct mode_tree_data *mtd, struct screen_write_ctx *ctx)
x = (sx - box_w) / 2;
y = (sy - box_h) / 2;
memcpy(&box_gc, &grid_default_cell, sizeof box_gc);
style_apply(&box_gc, oo, "tree-mode-border-style", NULL);
memcpy(&gc, &grid_default_cell, sizeof gc);
ft = format_create_defaults(NULL, NULL, NULL, NULL, mtd->wp);
screen_write_cursormove(ctx, x, y, 0);
screen_write_box(ctx, box_w, box_h, BOX_LINES_DEFAULT, &gc, NULL);
screen_write_box(ctx, box_w, box_h, BOX_LINES_DEFAULT, &box_gc, NULL);
y++;
x++;
for (line = mode_tree_help_start; *line != NULL; line++, y++)
mode_tree_draw_help_line(ctx, &gc, *line, item, x, y, w);
mode_tree_draw_help_line(ctx, &gc, ft, *line, item, x, y, w);
for (line = lines; line != NULL && *line != NULL; line++, y++)
mode_tree_draw_help_line(ctx, &gc, *line, item, x, y, w);
mode_tree_draw_help_line(ctx, &gc, ft, *line, item, x, y, w);
for (line = mode_tree_help_end; *line != NULL; line++, y++)
mode_tree_draw_help_line(ctx, &gc, *line, item, x, y, w);
mode_tree_draw_help_line(ctx, &gc, ft, *line, item, x, y, w);
format_free(ft);
}
static void
@@ -1251,15 +1498,75 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
{
struct mode_tree_line *line;
struct mode_tree_item *current, *parent, *mti;
u_int i, x, y;
u_int i, x, y, py, sx;
int choice, preview;
enum prompt_key_result result;
int redraw;
struct prompt *prompt;
struct mode_tree_prompt *mtp;
if (mtd->line_size == 0) {
*key = KEYC_NONE;
return (1);
}
if (mtd->prompt != NULL) {
redraw = 0;
prompt = mtd->prompt;
mtp = mtd->prompt_data;
if (mtp != NULL)
mtp->c = c;
if (KEYC_IS_MOUSE(*key)) {
if (m == NULL ||
MOUSE_BUTTONS(m->b) != MOUSE_BUTTON_1 ||
MOUSE_DRAG(m->b) || MOUSE_RELEASE(m->b) ||
cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0)
result = PROMPT_KEY_NOT_HANDLED;
else {
sx = screen_size_x(&mtd->screen);
if (mtd->prompt_top)
py = 0;
else
py = screen_size_y(&mtd->screen) - 1;
if (y == py) {
result = prompt_mouse(prompt, x, 0, sx,
&redraw);
} else
result = PROMPT_KEY_NOT_HANDLED;
}
} else
result = prompt_key(prompt, *key, &redraw);
if (mtd->prompt_data == mtp && mtp != NULL)
mtp->c = NULL;
/*
* Only an explicit close or the prompt marking itself closed
* ends it; cursor movement and editing keep it open.
*/
if (mtd->prompt == prompt &&
(result == PROMPT_KEY_CLOSE || prompt_closed(prompt)))
mode_tree_clear_prompt(mtd);
if (redraw || mtd->prompt != prompt) {
mode_tree_draw(mtd);
mtd->wp->flags |= PANE_REDRAW;
}
if (result != PROMPT_KEY_NOT_HANDLED) {
*key = KEYC_NONE;
return (0);
}
}
if (mtd->help) {
if (KEYC_IS_MOUSE(*key)) {
*key = KEYC_NONE;
return (0);
}
if (*key == KEYC_FOCUS_IN || *key == KEYC_FOCUS_OUT) {
*key = KEYC_NONE;
return (0);
}
mtd->help = 0;
mode_tree_draw(mtd);
*key = KEYC_NONE;
@@ -1461,11 +1768,10 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
case '?':
case '/':
case 's'|KEYC_CTRL:
mtd->references++;
mtd->search_dir = MODE_TREE_SEARCH_FORWARD;
status_prompt_set(c, NULL, "(search) ", "",
mode_tree_search_callback, mode_tree_search_free, mtd,
PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH);
mode_tree_set_prompt(mtd, c, "(search) ", "",
PROMPT_TYPE_SEARCH, PROMPT_NOFORMAT,
mode_tree_search_callback, NULL, mtd);
break;
case 'n':
mtd->search_dir = MODE_TREE_SEARCH_FORWARD;
@@ -1476,10 +1782,13 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
mode_tree_search_set(mtd);
break;
case 'f':
mtd->references++;
status_prompt_set(c, NULL, "(filter) ", mtd->filter,
mode_tree_filter_callback, mode_tree_filter_free, mtd,
PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH);
mode_tree_set_prompt(mtd, c, "(filter) ", mtd->filter,
PROMPT_TYPE_SEARCH, PROMPT_NOFORMAT,
mode_tree_filter_callback, NULL, mtd);
break;
case 'c':
mode_tree_clear_prompt(mtd);
mode_tree_clear_filter(mtd);
break;
case 'v':
switch (mtd->preview) {

View File

@@ -95,7 +95,7 @@ check_window_name(struct window *w)
name = format_window_name(w);
if (strcmp(name, w->name) != 0) {
log_debug("@%u new name %s (was %s)", w->id, name, w->name);
window_set_name(w, name, WINDOW_NAME_FORBID_EXT);
window_set_name(w, name, 1);
server_redraw_window_borders(w);
server_status_window(w);
} else
@@ -166,7 +166,7 @@ parse_window_name(const char *in)
if (*name == '/')
name = basename(name);
name = clean_name(name, WINDOW_NAME_FORBID);
name = clean_name(name, 0);
free(copy);
if (name == NULL)
return (xstrdup(""));

View File

@@ -64,7 +64,7 @@ static const char *options_table_cursor_style_list[] = {
"blinking-bar", "bar", NULL
};
static const char *options_table_pane_scrollbars_list[] = {
"off", "modal", "on", NULL
"off", "modal", "on", "auto-hide", NULL
};
static const char *options_table_pane_scrollbars_position_list[] = {
"right", "left", NULL
@@ -108,6 +108,9 @@ static const char *options_table_extended_keys_format_list[] = {
static const char *options_table_allow_passthrough_list[] = {
"off", "on", "all", NULL
};
static const char *options_table_theme_list[] = {
"detect", "terminal", "light", "dark", NULL
};
static const char *options_table_copy_mode_line_numbers_list[] = {
"off", "default", "absolute", "relative", "hybrid", NULL
};
@@ -270,6 +273,7 @@ const struct options_name_map options_other_names[] = {
{ "clock-mode-color", "clock-mode-colour" },
{ "cursor-color", "cursor-colour" },
{ "prompt-cursor-color", "prompt-cursor-colour" },
{ "prompt-command-cursor-color", "prompt-command-cursor-colour" },
{ "pane-colors", "pane-colours" },
{ NULL, NULL }
};
@@ -327,9 +331,10 @@ const struct options_table_entry options_table[] = {
},
{ .name = "cursor-colour",
.type = OPTIONS_TABLE_COLOUR,
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_num = -1,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "",
.text = "Colour of the cursor."
},
@@ -444,7 +449,7 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.flags = OPTIONS_TABLE_IS_STYLE,
.default_str = "default",
.default_str = "bg=themedarkgrey,fg=themewhite",
.separator = ",",
.text = "Default style of menu."
},
@@ -453,7 +458,7 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.flags = OPTIONS_TABLE_IS_STYLE,
.default_str = "bg=yellow,fg=black",
.default_str = "bg=themeyellow,fg=themeblack",
.separator = ",",
.text = "Default style of selected menu item."
},
@@ -461,7 +466,7 @@ const struct options_table_entry options_table[] = {
{ .name = "menu-border-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "default",
.default_str = "bg=themedarkgrey,fg=themelightgrey",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Default style of menu borders."
@@ -536,6 +541,175 @@ const struct options_table_entry options_table[] = {
"automatically detected."
},
{ .name = "theme",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SERVER,
.choices = options_table_theme_list,
.default_num = 0,
.text = "Whether tmux should detect the terminal theme, use terminal "
"ANSI colours, or force the light or dark theme."
},
{ .name = "dark-theme-black",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},gray5,black}",
.text = "Dark theme colour for black."
},
{ .name = "dark-theme-white",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},gray90,white}",
.text = "Dark theme colour for white."
},
{ .name = "dark-theme-light-grey",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},gray70,white}",
.text = "Dark theme colour for light grey."
},
{ .name = "dark-theme-dark-grey",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},gray15,black}",
.text = "Dark theme colour for dark grey."
},
{ .name = "dark-theme-green",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},yellowgreen,green}",
.text = "Dark theme colour for green."
},
{ .name = "dark-theme-yellow",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},darkgoldenrod,yellow}",
.text = "Dark theme colour for yellow."
},
{ .name = "dark-theme-red",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},indianred,red}",
.text = "Dark theme colour for red."
},
{ .name = "dark-theme-blue",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},skyblue3,blue}",
.text = "Dark theme colour for blue."
},
{ .name = "dark-theme-cyan",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},cadetblue,cyan}",
.text = "Dark theme colour for cyan."
},
{ .name = "dark-theme-magenta",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},mediumpurple,magenta}",
.text = "Dark theme colour for magenta."
},
{ .name = "light-theme-black",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},gray10,black}",
.text = "Light theme colour for black."
},
{ .name = "light-theme-white",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},gray95,white}",
.text = "Light theme colour for white."
},
{ .name = "light-theme-light-grey",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},gray80,white}",
.text = "Light theme colour for light grey."
},
{ .name = "light-theme-dark-grey",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},gray45,black}",
.text = "Light theme colour for dark grey."
},
{ .name = "light-theme-green",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},seagreen,green}",
.text = "Light theme colour for green."
},
{ .name = "light-theme-yellow",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},darkgoldenrod,yellow}",
.text = "Light theme colour for yellow."
},
{ .name = "light-theme-red",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},indianred4,red}",
.text = "Light theme colour for red."
},
{ .name = "light-theme-blue",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},steelblue,blue}",
.text = "Light theme colour for blue."
},
{ .name = "light-theme-cyan",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},darkcyan,cyan}",
.text = "Light theme colour for cyan."
},
{ .name = "light-theme-magenta",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "#{?#{e|>=:#{client_colours},256},purple4,magenta}",
.text = "Light theme colour for magenta."
},
{ .name = "user-keys",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
@@ -634,16 +808,18 @@ const struct options_table_entry options_table[] = {
},
{ .name = "display-panes-active-colour",
.type = OPTIONS_TABLE_COLOUR,
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 1,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "themered",
.text = "Colour of the active pane for 'display-panes'."
},
{ .name = "display-panes-colour",
.type = OPTIONS_TABLE_COLOUR,
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_num = 4,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "themeblue",
.text = "Colour of not active panes for 'display-panes'."
},
@@ -734,7 +910,9 @@ const struct options_table_entry options_table[] = {
{ .name = "message-command-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "bg=black,fg=yellow,fill=black",
.default_str = "bg=themeblack,fg=themeyellow,"
"#{?#{m/r:(^|#,)IS(PANE|MODE)($|#,),#{prompt_flags}},,"
"fill=themeblack}",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the command prompt when in command mode, if "
@@ -763,7 +941,9 @@ const struct options_table_entry options_table[] = {
{ .name = "message-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "bg=yellow,fg=black,fill=yellow",
.default_str = "bg=themeyellow,fg=themeblack,"
"#{?#{m/r:(^|#,)IS(PANE|MODE)($|#,),#{prompt_flags}},,"
"fill=themeyellow}",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of messages and the command prompt. "
@@ -962,7 +1142,7 @@ const struct options_table_entry options_table[] = {
{ .name = "status-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "bg=green,fg=black",
.default_str = "bg=themegreen,fg=themeblack",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the status line."
@@ -971,7 +1151,7 @@ const struct options_table_entry options_table[] = {
{ .name = "pane-status-current-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "default",
.default_str = "underscore",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the current pane in the status line."
@@ -988,12 +1168,22 @@ const struct options_table_entry options_table[] = {
},
{ .name = "prompt-cursor-colour",
.type = OPTIONS_TABLE_COLOUR,
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_num = -1,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "",
.text = "Colour of the cursor when in the command prompt."
},
{ .name = "prompt-command-cursor-colour",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "",
.text = "Colour of the cursor in the command prompt when in command "
"mode, if 'status-keys' is set to 'vi'."
},
{ .name = "prompt-cursor-style",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
@@ -1014,7 +1204,7 @@ const struct options_table_entry options_table[] = {
{ .name = "session-status-current-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "default",
.default_str = "underscore",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the current session in the status line."
@@ -1142,9 +1332,10 @@ const struct options_table_entry options_table[] = {
},
{ .name = "clock-mode-colour",
.type = OPTIONS_TABLE_COLOUR,
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_num = 4,
.flags = OPTIONS_TABLE_IS_COLOUR,
.default_str = "themeblue",
.text = "Colour of the clock in clock mode."
},
@@ -1159,7 +1350,7 @@ const struct options_table_entry options_table[] = {
{ .name = "copy-mode-match-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "bg=cyan,fg=black",
.default_str = "bg=themecyan,fg=themeblack",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of search matches in copy mode."
@@ -1168,7 +1359,7 @@ const struct options_table_entry options_table[] = {
{ .name = "copy-mode-current-match-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "bg=magenta,fg=black",
.default_str = "bg=thememagenta,fg=themeblack",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the current search match in copy mode."
@@ -1177,7 +1368,7 @@ const struct options_table_entry options_table[] = {
{ .name = "copy-mode-mark-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "bg=red,fg=black",
.default_str = "bg=themeyellow,fg=themeblack",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the marked line in copy mode."
@@ -1216,7 +1407,7 @@ const struct options_table_entry options_table[] = {
{ .name = "copy-mode-current-line-number-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "fg=yellow",
.default_str = "fg=themeyellow",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of current line number in copy mode."
@@ -1225,7 +1416,7 @@ const struct options_table_entry options_table[] = {
{ .name = "copy-mode-line-number-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "fg=white,dim",
.default_str = "fg=themelightgrey,dim",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of line numbers in copy mode."
@@ -1296,7 +1487,7 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.flags = OPTIONS_TABLE_IS_STYLE,
.default_str = "noattr,bg=yellow,fg=black",
.default_str = "noattr,bg=themeyellow,fg=themeblack",
.separator = ",",
.text = "Style of indicators and highlighting in modes."
},
@@ -1345,7 +1536,9 @@ const struct options_table_entry options_table[] = {
{ .name = "pane-active-border-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "#{?pane_in_mode,fg=yellow,#{?synchronize-panes,fg=red,fg=green}}",
.default_str = "fg=#{?pane_marked,thememagenta,"
"#{?synchronize-panes,themered,"
"#{?pane_in_mode,themeyellow,themegreen}}}",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the active pane border."
@@ -1404,7 +1597,7 @@ const struct options_table_entry options_table[] = {
{ .name = "pane-border-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "default",
.default_str = "fg=themelightgrey",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the pane status lines."
@@ -1423,13 +1616,23 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_pane_scrollbars_list,
.default_num = PANE_SCROLLBARS_OFF,
.text = "Pane scrollbar state."
.text = "Pane scrollbar state: off, on, modal, or auto-hide."
},
{ .name = "pane-scrollbars-timeout",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_WINDOW,
.minimum = 0,
.maximum = INT_MAX,
.default_num = 500,
.unit = "milliseconds",
.text = "Time before modal and auto-hide pane scrollbars disappear."
},
{ .name = "pane-scrollbars-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "bg=black,fg=white,width=1,pad=0",
.default_str = "bg=themedarkgrey,fg=themelightgrey,width=1,pad=0",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the pane scrollbar."
@@ -1446,7 +1649,7 @@ const struct options_table_entry options_table[] = {
{ .name = "popup-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "default",
.default_str = "bg=themedarkgrey,fg=themewhite",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Default style of popups."
@@ -1455,7 +1658,7 @@ const struct options_table_entry options_table[] = {
{ .name = "popup-border-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "default",
.default_str = "bg=themedarkgrey,fg=themelightgrey",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Default style of popup borders."
@@ -1501,6 +1704,15 @@ const struct options_table_entry options_table[] = {
"history when clearing the whole screen."
},
{ .name = "switch-mode-match-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "bg=cyan fg=black",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of matched characters in switch mode."
},
{ .name = "synchronize-panes",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
@@ -1518,6 +1730,15 @@ const struct options_table_entry options_table[] = {
"A value of 0 means no limit."
},
{ .name = "tree-mode-border-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "bg=themedarkgrey,fg=themelightgrey",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of borders in tree mode."
},
{ .name = "tree-mode-preview-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
@@ -1533,13 +1754,22 @@ const struct options_table_entry options_table[] = {
.default_str = "fg=#{?#{||:"
"#{&&:#{pane_format},#{pane_active}},"
"#{&&:#{window_format},#{window_active}}},"
"#{display-panes-active-colour},"
"#{display-panes-colour}}",
"themered,"
"themeblue}",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of preview indicator in tree mode."
},
{ .name = "tree-mode-selection-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "#{E:mode-style}",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the selected line in tree mode."
},
{ .name = "window-active-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
@@ -1613,7 +1843,7 @@ const struct options_table_entry options_table[] = {
{ .name = "window-status-current-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "default",
.default_str = "underscore",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the current window in the status line."

View File

@@ -662,6 +662,18 @@ options_parse_get(struct options *oo, const char *s, int *idx, int only)
return (o);
}
const struct options_table_entry *
options_search(const char *name)
{
const struct options_table_entry *oe;
for (oe = options_table; oe->name != NULL; oe++) {
if (strcmp(oe->name, name) == 0)
return (oe);
}
return (NULL);
}
char *
options_match(const char *s, int *idx, int *ambiguous)
{
@@ -977,9 +989,12 @@ struct style *
options_string_to_style(struct options *oo, const char *name,
struct format_tree *ft)
{
struct options_entry *o;
const char *s;
char *expanded;
struct options_entry *o;
const struct options_table_entry *oe;
const struct grid_cell *dgc = &grid_default_cell;
const char *s;
char *expanded;
int failed;
o = options_get(oo, name);
if (o == NULL || !OPTIONS_IS_STRING(o))
@@ -988,20 +1003,27 @@ options_string_to_style(struct options *oo, const char *name,
if (o->cached)
return (&o->style);
s = o->value.string;
oe = o->tableentry;
log_debug("%s: %s is '%s'", __func__, name, s);
style_set(&o->style, &grid_default_cell);
style_set(&o->style, dgc);
o->cached = (strstr(s, "#{") == NULL);
if (ft != NULL && !o->cached) {
expanded = format_expand(ft, s);
if (style_parse(&o->style, &grid_default_cell, expanded) != 0) {
free(expanded);
return (NULL);
}
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_COLOUR))
failed = style_parse_colour(&o->style, dgc, expanded);
else
failed = style_parse(&o->style, dgc, expanded);
free(expanded);
if (failed != 0)
return (NULL);
} else {
if (style_parse(&o->style, &grid_default_cell, s) != 0)
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_COLOUR))
failed = style_parse_colour(&o->style, dgc, s);
else
failed = style_parse(&o->style, dgc, s);
if (failed != 0)
return (NULL);
}
return (&o->style);
@@ -1029,6 +1051,12 @@ options_from_string_check(const struct options_table_entry *oe,
xasprintf(cause, "invalid style: %s", value);
return (-1);
}
if ((oe->flags & OPTIONS_TABLE_IS_COLOUR) &&
strstr(value, "#{") == NULL &&
style_parse_colour(&sy, &grid_default_cell, value) != 0) {
xasprintf(cause, "invalid colour: %s", value);
return (-1);
}
return (0);
}
@@ -1186,6 +1214,17 @@ options_push_changes(const char *name)
log_debug("%s: %s", __func__, name);
if (strcmp(name, "theme") == 0 ||
strncmp(name, "dark-theme-", 11) == 0 ||
strncmp(name, "light-theme-", 12) == 0) {
TAILQ_FOREACH(loop, &clients, entry) {
server_client_update_theme_colours(loop);
if (loop->tty.flags & TTY_OPENED)
tty_invalidate(&loop->tty);
server_redraw_client(loop);
}
}
if (strcmp(name, "automatic-rename") == 0) {
RB_FOREACH(w, windows, &windows) {
if (w->active == NULL)
@@ -1219,6 +1258,16 @@ options_push_changes(const char *name)
if (strcmp(name, "status") == 0 ||
strcmp(name, "status-interval") == 0)
status_timer_start_all();
if (strcmp(name, "status") == 0 ||
strcmp(name, "status-position") == 0 ||
strcmp(name, "pane-border-indicators") == 0 ||
strcmp(name, "pane-border-lines") == 0 ||
strcmp(name, "pane-border-status") == 0 ||
strcmp(name, "pane-scrollbars") == 0 ||
strcmp(name, "pane-scrollbars-timeout") == 0 ||
strcmp(name, "pane-scrollbars-position") == 0 ||
strcmp(name, "pane-scrollbars-style") == 0)
redraw_invalidate_all_scenes();
if (strcmp(name, "monitor-silence") == 0)
alerts_reset_all();
if (strcmp(name, "window-style") == 0 ||
@@ -1237,8 +1286,17 @@ options_push_changes(const char *name)
if (strcmp(name, "pane-border-status") == 0 ||
strcmp(name, "pane-scrollbars") == 0 ||
strcmp(name, "pane-scrollbars-position") == 0) {
RB_FOREACH(w, windows, &windows)
RB_FOREACH(w, windows, &windows) {
w->sb = options_get_number(w->options,
"pane-scrollbars");
w->sb_pos = options_get_number(w->options,
"pane-scrollbars-position");
layout_fix_panes(w, NULL);
}
}
if (strcmp(name, "pane-scrollbars") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
window_pane_scrollbar_hide(wp);
}
if (strcmp(name, "pane-scrollbars-style") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {

View File

@@ -219,7 +219,7 @@ paste_rename(const char *oldname, const char *newname, char **cause)
return (-1);
}
name = clean_name(newname, "");
name = clean_name(newname, 0);
if (name == NULL) {
if (cause != NULL)
xasprintf(cause, "invalid buffer name: %s", newname);
@@ -287,7 +287,7 @@ paste_set(char *data, size_t size, const char *name, char **cause)
return (-1);
}
newname = clean_name(name, "");
newname = clean_name(name, 0);
if (newname == NULL) {
if (cause != NULL)
xasprintf(cause, "invalid buffer name: %s", name);

View File

@@ -273,7 +273,7 @@ popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx)
}
static void
popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx)
popup_draw_cb(struct client *c, void *data)
{
struct popup_data *pd = data;
struct tty *tty = &c->tty;
@@ -328,7 +328,7 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx)
if (pd->md != NULL) {
c->overlay_check = NULL;
c->overlay_data = NULL;
menu_draw_cb(c, pd->md, rctx);
menu_draw_cb(c, pd->md);
}
c->overlay_check = popup_check_cb;
c->overlay_data = pd;

264
prompt-history.c Normal file
View File

@@ -0,0 +1,264 @@
/* $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 <errno.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
static char *prompt_find_history_file(void);
static void prompt_add_typed_history(char *);
/* Prompt history. */
static char **prompt_hlist[PROMPT_NTYPES];
static u_int prompt_hsize[PROMPT_NTYPES];
/* Find the history file to load/save from/to. */
static char *
prompt_find_history_file(void)
{
const char *home, *history_file;
char *path;
history_file = options_get_string(global_options, "history-file");
if (*history_file == '\0')
return (NULL);
if (*history_file == '/')
return (xstrdup(history_file));
if (history_file[0] != '~' || history_file[1] != '/')
return (NULL);
if ((home = find_home()) == NULL)
return (NULL);
xasprintf(&path, "%s%s", home, history_file + 1);
return (path);
}
/* Add loaded history item to the appropriate list. */
static void
prompt_add_typed_history(char *line)
{
char *typestr;
enum prompt_type type = PROMPT_TYPE_INVALID;
typestr = strsep(&line, ":");
if (line != NULL)
type = prompt_type(typestr);
if (type == PROMPT_TYPE_INVALID) {
/*
* Invalid types are not expected, but this provides backward
* compatibility with old history files.
*/
if (line != NULL)
*(--line) = ':';
prompt_add_history(typestr, PROMPT_TYPE_COMMAND);
} else
prompt_add_history(line, type);
}
/* Load prompt history from file. */
void
prompt_load_history(void)
{
FILE *f;
char *history_file, *line, *tmp;
size_t length;
if ((history_file = prompt_find_history_file()) == NULL)
return;
log_debug("loading history from %s", history_file);
f = fopen(history_file, "r");
if (f == NULL) {
log_debug("%s: %s", history_file, strerror(errno));
free(history_file);
return;
}
free(history_file);
for (;;) {
if ((line = fgetln(f, &length)) == NULL)
break;
if (length > 0) {
if (line[length - 1] == '\n') {
line[length - 1] = '\0';
prompt_add_typed_history(line);
} else {
tmp = xmalloc(length + 1);
memcpy(tmp, line, length);
tmp[length] = '\0';
prompt_add_typed_history(tmp);
free(tmp);
}
}
}
fclose(f);
}
/* Save prompt history to file. */
void
prompt_save_history(void)
{
FILE *f;
u_int i, type;
char *history_file;
if ((history_file = prompt_find_history_file()) == NULL)
return;
log_debug("saving history to %s", history_file);
f = fopen(history_file, "w");
if (f == NULL) {
log_debug("%s: %s", history_file, strerror(errno));
free(history_file);
return;
}
free(history_file);
for (type = 0; type < PROMPT_NTYPES; type++) {
for (i = 0; i < prompt_hsize[type]; i++) {
fputs(prompt_type_string(type), f);
fputc(':', f);
fputs(prompt_hlist[type][i], f);
fputc('\n', f);
}
}
fclose(f);
}
/* Get previous line from the history. */
const char *
prompt_up_history(u_int *idx, u_int type)
{
/*
* History runs from 0 to size - 1. Index is from 0 to size. Zero is
* empty.
*/
if (type >= PROMPT_NTYPES)
return (NULL);
if (prompt_hsize[type] == 0 || idx[type] == prompt_hsize[type])
return (NULL);
idx[type]++;
return (prompt_hlist[type][prompt_hsize[type] - idx[type]]);
}
/* Get next line from the history. */
const char *
prompt_down_history(u_int *idx, u_int type)
{
if (type >= PROMPT_NTYPES)
return ("");
if (prompt_hsize[type] == 0 || idx[type] == 0)
return ("");
idx[type]--;
if (idx[type] == 0)
return ("");
return (prompt_hlist[type][prompt_hsize[type] - idx[type]]);
}
/* Add line to the history. */
void
prompt_add_history(const char *line, u_int type)
{
u_int i, oldsize, newsize, freecount, hlimit, new = 1;
size_t movesize;
if (type >= PROMPT_NTYPES)
return;
oldsize = prompt_hsize[type];
if (oldsize > 0 &&
strcmp(prompt_hlist[type][oldsize - 1], line) == 0)
new = 0;
hlimit = options_get_number(global_options, "prompt-history-limit");
if (hlimit > oldsize) {
if (new == 0)
return;
newsize = oldsize + new;
} else {
newsize = hlimit;
freecount = oldsize + new - newsize;
if (freecount > oldsize)
freecount = oldsize;
if (freecount == 0)
return;
for (i = 0; i < freecount; i++)
free(prompt_hlist[type][i]);
movesize = (oldsize - freecount) *
sizeof *prompt_hlist[type];
if (movesize > 0) {
memmove(&prompt_hlist[type][0],
&prompt_hlist[type][freecount], movesize);
}
}
if (newsize == 0) {
free(prompt_hlist[type]);
prompt_hlist[type] = NULL;
} else if (newsize != oldsize) {
prompt_hlist[type] =
xreallocarray(prompt_hlist[type], newsize,
sizeof *prompt_hlist[type]);
}
if (new == 1 && newsize > 0)
prompt_hlist[type][newsize - 1] = xstrdup(line);
prompt_hsize[type] = newsize;
}
/* Get history size. */
u_int
prompt_history_size(enum prompt_type type)
{
if (type >= PROMPT_NTYPES)
return (0);
return (prompt_hsize[type]);
}
/* Get history entry. */
const char *
prompt_history_get(enum prompt_type type, u_int idx)
{
if (type >= PROMPT_NTYPES)
return (NULL);
if (idx >= prompt_hsize[type])
return (NULL);
return (prompt_hlist[type][idx]);
}
/* Clear prompt history. */
void
prompt_history_clear(enum prompt_type type)
{
u_int idx;
if (type >= PROMPT_NTYPES)
return;
for (idx = 0; idx < prompt_hsize[type]; idx++)
free(prompt_hlist[type][idx]);
free(prompt_hlist[type]);
prompt_hlist[type] = NULL;
prompt_hsize[type] = 0;
}

1605
prompt.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -110,20 +110,26 @@ must_fail $TMUX set-buffer -b "bad${invalid}name" data
# Titles set by commands allow '#', ':' and '.'.
$TMUX select-pane -T 'title#:.ok' || fail "command title rejected"
must_equal "$($TMUX display-message -p '#{pane_title}')" 'title#:.ok'
$TMUX send-keys "printf '\\033]2;title#[fg=red]ok\\007'" Enter || exit 1
sleep 1
must_equal "$($TMUX display-message -p '#{pane_title}')" 'title#[fg=red]ok'
$TMUX send-keys "printf '\\033]2;title#(bad)\\007'" Enter || exit 1
sleep 1
must_equal "$($TMUX display-message -p '#{pane_title}')" 'title_(bad)'
# Buffer names allow '#', ':' and '.'.
$TMUX set-buffer -b 'buffer#:.ok' data || fail "buffer name rejected"
must_equal "$($TMUX list-buffers -F '#{buffer_name}')" 'buffer#:.ok'
# Window names from escape sequences reject '#', ':' and '.' by cleaning them.
# Window names from escape sequences allow '#' except in '#('.
$TMUX send-keys "printf '\\033kescape#:.ok\\033\\\\'" Enter || exit 1
sleep 1
must_equal "$($TMUX display-message -p '#{window_name}')" 'escape___ok'
must_equal "$($TMUX display-message -p '#{window_name}')" 'escape#__ok'
# Titles from escape sequences reject only '#'.
$TMUX send-keys "printf '\\033]2;escape#:.ok\\007'" Enter || exit 1
sleep 1
must_equal "$($TMUX display-message -p '#{pane_title}')" 'escape_:.ok'
must_equal "$($TMUX display-message -p '#{pane_title}')" 'escape#:.ok'
# Invalid UTF-8 from escape sequences is ignored.
$TMUX rename-window 'before-invalid' || exit 1

View File

@@ -0,0 +1,203 @@
#!/bin/sh
# Test new-pane, resize-pane, and move-pane -x/-y/-X/-Y with floating panes.
# Verifies that size and position are correctly applied with the default border,
# with -B none, and with the pane-border-lines window option set to none. Also
# verifies that zero sizes are rejected.
#
# With a border, -x/-y/-X/-Y specify the onscreen footprint including the
# border: new-pane subtracts 2 from width/height and adds 1 to x/y-position
# so that the border lands on the specified screen coordinates. resize-pane
# -x/-y similarly subtracts 2 from the requested size. move-pane -X/-Y adds 1
# to the position. Without a border all values are used directly.
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
$TMUX kill-server 2>/dev/null
fail()
{
echo "$*" >&2
$TMUX kill-server 2>/dev/null
exit 1
}
must_fail()
{
"$@" >/dev/null 2>&1 && fail "unexpected success: $*"
return 0
}
must_equal()
{
got=$1
want=$2
[ "$got" = "$want" ] || fail "got '$got', expected '$want'"
}
$TMUX new-session -d -x 80 -y 24 || exit 1
# --- Default border (single-line) ---
# Explicit values: border subtracts 2 from size and adds 1 to position.
# -x 20 -> pane_width 18; -y 6 -> pane_height 4;
# -X 8 -> pane_left 9; -Y 3 -> pane_top 4.
id=$($TMUX new-pane -dPF '#{pane_id}' -x 20 -y 6 -X 8 -Y 3 'sleep 100') \
|| fail "new-pane with explicit geometry failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_width}')" 18
must_equal "$($TMUX display-message -p -t "$id" '#{pane_height}')" 4
must_equal "$($TMUX display-message -p -t "$id" '#{pane_left}')" 9
must_equal "$($TMUX display-message -p -t "$id" '#{pane_top}')" 4
# resize-pane with border: requested size decremented by 2.
$TMUX resize-pane -t "$id" -x 30 || fail "resize-pane -x 30 failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_width}')" 28
$TMUX resize-pane -t "$id" -y 10 || fail "resize-pane -y 10 failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_height}')" 8
# resize-pane with percentage and border.
# 75% of 80 = 60, 60 - 2 = 58; 75% of 24 = 18, 18 - 2 = 16.
$TMUX resize-pane -t "$id" -x 75% || fail "resize-pane -x 75% failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_width}')" 58
$TMUX resize-pane -t "$id" -y 75% || fail "resize-pane -y 75% failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_height}')" 16
$TMUX kill-pane -t "$id" || exit 1
# Percentage width, height and position with border.
# 50% of 80 = 40, 40-2 = 38; 50% of 24 = 12, 12-2 = 10.
# 10% of 80 = 8, 8+1 = 9; 10% of 24 = 2, 2+1 = 3.
id=$($TMUX new-pane -dPF '#{pane_id}' -x 50% -y 50% -X 10% -Y 10% 'sleep 100') \
|| fail "new-pane with percentage geometry failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_width}')" 38
must_equal "$($TMUX display-message -p -t "$id" '#{pane_height}')" 10
must_equal "$($TMUX display-message -p -t "$id" '#{pane_left}')" 9
must_equal "$($TMUX display-message -p -t "$id" '#{pane_top}')" 3
# move-pane -X/-Y with border: border lands at the given column/row, content
# is one cell inside. -X 5 -> pane_left 6; -Y 1 -> pane_top 2.
$TMUX move-pane -t "$id" -X 5 || fail "move-pane -X 5 failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_left}')" 6
$TMUX move-pane -t "$id" -Y 1 || fail "move-pane -Y 1 failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_top}')" 2
# 25% of 80 = 20, 20+1 = 21; 25% of 24 = 6, 6+1 = 7.
$TMUX move-pane -t "$id" -X 25% || fail "move-pane -X 25% failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_left}')" 21
$TMUX move-pane -t "$id" -Y 25% || fail "move-pane -Y 25% failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_top}')" 7
$TMUX kill-pane -t "$id" || exit 1
# --- -B none ---
# Without a border all values are used directly.
id=$($TMUX new-pane -dPF '#{pane_id}' -B none -x 20 -y 6 -X 8 -Y 3 'sleep 100') \
|| fail "new-pane -B none with explicit geometry failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_width}')" 20
must_equal "$($TMUX display-message -p -t "$id" '#{pane_height}')" 6
must_equal "$($TMUX display-message -p -t "$id" '#{pane_left}')" 8
must_equal "$($TMUX display-message -p -t "$id" '#{pane_top}')" 3
# resize-pane without border: size used directly.
$TMUX resize-pane -t "$id" -x 30 || fail "resize-pane -x 30 failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_width}')" 30
$TMUX resize-pane -t "$id" -y 10 || fail "resize-pane -y 10 failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_height}')" 10
# resize-pane with percentage, no border: 75% of 80 = 60; 75% of 24 = 18.
$TMUX resize-pane -t "$id" -x 75% || fail "resize-pane -x 75% failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_width}')" 60
$TMUX resize-pane -t "$id" -y 75% || fail "resize-pane -y 75% failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_height}')" 18
$TMUX kill-pane -t "$id" || exit 1
# Percentage geometry without border: 50% of 80 = 40; 10% of 80 = 8 etc.
id=$($TMUX new-pane -dPF '#{pane_id}' -B none -x 50% -y 50% -X 10% -Y 10% 'sleep 100') \
|| fail "new-pane -B none with percentage geometry failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_width}')" 40
must_equal "$($TMUX display-message -p -t "$id" '#{pane_height}')" 12
must_equal "$($TMUX display-message -p -t "$id" '#{pane_left}')" 8
must_equal "$($TMUX display-message -p -t "$id" '#{pane_top}')" 2
# move-pane without border: -X/-Y set the content position directly.
# -X 5 -> pane_left 5; -Y 1 -> pane_top 1.
$TMUX move-pane -t "$id" -X 5 || fail "move-pane -X 5 failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_left}')" 5
$TMUX move-pane -t "$id" -Y 1 || fail "move-pane -Y 1 failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_top}')" 1
# 25% of 80 = 20; 25% of 24 = 6.
$TMUX move-pane -t "$id" -X 25% || fail "move-pane -X 25% failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_left}')" 20
$TMUX move-pane -t "$id" -Y 25% || fail "move-pane -Y 25% failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_top}')" 6
$TMUX kill-pane -t "$id" || exit 1
# --- Window option pane-border-lines none ---
$TMUX set-option -w pane-border-lines none || exit 1
# Inherits none from window option: same behaviour as -B none.
id=$($TMUX new-pane -dPF '#{pane_id}' -x 20 -y 6 -X 8 -Y 3 'sleep 100') \
|| fail "new-pane with window pane-border-lines none failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_width}')" 20
must_equal "$($TMUX display-message -p -t "$id" '#{pane_height}')" 6
must_equal "$($TMUX display-message -p -t "$id" '#{pane_left}')" 8
must_equal "$($TMUX display-message -p -t "$id" '#{pane_top}')" 3
$TMUX resize-pane -t "$id" -x 30 || fail "resize-pane -x 30 failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_width}')" 30
$TMUX resize-pane -t "$id" -y 10 || fail "resize-pane -y 10 failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_height}')" 10
$TMUX resize-pane -t "$id" -x 75% || fail "resize-pane -x 75% failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_width}')" 60
$TMUX resize-pane -t "$id" -y 75% || fail "resize-pane -y 75% failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_height}')" 18
$TMUX kill-pane -t "$id" || exit 1
id=$($TMUX new-pane -dPF '#{pane_id}' -x 50% -y 50% -X 10% -Y 10% 'sleep 100') \
|| fail "new-pane with window pane-border-lines none and percentages failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_width}')" 40
must_equal "$($TMUX display-message -p -t "$id" '#{pane_height}')" 12
must_equal "$($TMUX display-message -p -t "$id" '#{pane_left}')" 8
must_equal "$($TMUX display-message -p -t "$id" '#{pane_top}')" 2
# move-pane with window pane-border-lines none: same behaviour as -B none.
$TMUX move-pane -t "$id" -X 5 || fail "move-pane -X 5 failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_left}')" 5
$TMUX move-pane -t "$id" -Y 1 || fail "move-pane -Y 1 failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_top}')" 1
$TMUX move-pane -t "$id" -X 25% || fail "move-pane -X 25% failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_left}')" 20
$TMUX move-pane -t "$id" -Y 25% || fail "move-pane -Y 25% failed"
must_equal "$($TMUX display-message -p -t "$id" '#{pane_top}')" 6
$TMUX kill-pane -t "$id" || exit 1
# --- Invalid sizes ---
# Zero width and height must be rejected by new-pane with the default border.
$TMUX set-option -w -u pane-border-lines || exit 1
must_fail $TMUX new-pane -d -x 0 -y 6 -X 8 -Y 3 'sleep 100'
must_fail $TMUX new-pane -d -x 20 -y 0 -X 8 -Y 3 'sleep 100'
# Zero width and height must be rejected by resize-pane.
id=$($TMUX new-pane -dPF '#{pane_id}' -x 20 -y 6 -X 8 -Y 3 'sleep 100') \
|| fail "new-pane for resize-pane invalid size tests failed"
must_fail $TMUX resize-pane -t "$id" -x 0
must_fail $TMUX resize-pane -t "$id" -y 0
$TMUX kill-pane -t "$id" || exit 1
# Same rejections apply with no border.
$TMUX set-option -w pane-border-lines none || exit 1
must_fail $TMUX new-pane -d -x 0 -y 6 -X 8 -Y 3 'sleep 100'
must_fail $TMUX new-pane -d -x 20 -y 0 -X 8 -Y 3 'sleep 100'
id=$($TMUX new-pane -dPF '#{pane_id}' -x 20 -y 6 -X 8 -Y 3 'sleep 100') \
|| fail "new-pane for resize-pane invalid size tests (no border) failed"
must_fail $TMUX resize-pane -t "$id" -x 0
must_fail $TMUX resize-pane -t "$id" -y 0
$TMUX kill-pane -t "$id" || exit 1
$TMUX kill-server 2>/dev/null
exit 0

106
regress/format-fuzzy.sh Normal file
View File

@@ -0,0 +1,106 @@
#!/bin/sh
# Tests of fuzzy format matching.
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
# test_format $format $expected_result
test_format()
{
fmt="$1"
exp="$2"
out=$($TMUX display-message -p "$fmt")
if [ "$out" != "$exp" ]; then
echo "Fuzzy format test failed for '$fmt'."
echo "Expected: '$exp'"
echo "But got '$out'"
exit 1
fi
}
$TMUX kill-server 2>/dev/null
$TMUX -f/dev/null new-session -d || exit 1
# Empty pattern matches everything but has no highlighted positions.
test_format '#{m/z:,abc}' '1'
test_format '#{m/p:,abc}' ''
# Plain fuzzy matching and positions.
test_format '#{m/z:abc,a_b_c}' '1'
test_format '#{m/p:abc,a_b_c}' '0,2,4'
test_format '#{m/z:abc,acb}' '0'
test_format '#{m/p:abc,acb}' ''
# Multiple terms are ANDed. Positions are for all positive matched terms.
test_format '#{m/z:dev bash,dev:1 bash}' '1'
test_format '#{m/p:dev bash,dev:1 bash}' '0,1,2,6,7,8,9'
test_format '#{m/z:dev bash,dev:1 sh}' '0'
test_format '#{m/p:dev bash,dev:1 sh}' ''
# Lowercase patterns are case-insensitive for ASCII; uppercase makes the
# pattern case-sensitive.
test_format '#{m/z:abc,ABC}' '1'
test_format '#{m/p:abc,ABC}' '0,1,2'
test_format '#{m/z:ABC,abc}' '0'
test_format '#{m/p:ABC,abc}' ''
# Exact substring matching with a leading quote.
test_format "#{m/z:'bash,dev bash}" '1'
test_format "#{m/p:'bash,dev bash}" '4,5,6,7'
test_format "#{m/z:'bash,b-a-s-h}" '0'
test_format "#{m/p:'bash,b-a-s-h}" ''
# Prefix and suffix matching.
test_format '#{m/z:^dev,dev bash}' '1'
test_format '#{m/p:^dev,dev bash}' '0,1,2'
test_format '#{m/z:^dev,prod dev}' '0'
test_format '#{m/p:^dev,prod dev}' ''
test_format '#{m/z:bash$,dev bash}' '1'
test_format '#{m/p:bash$,dev bash}' '4,5,6,7'
test_format '#{m/z:bash$,bash dev}' '0'
test_format '#{m/p:bash$,bash dev}' ''
# Inverse terms. Plain inverse terms are exact substring tests, not fuzzy.
test_format '#{m/z:!ssh,dev bash}' '1'
test_format '#{m/z:!ssh,dev ssh}' '0'
test_format '#{m/z:!ssh,s_s_h}' '1'
test_format '#{m/z:dev !ssh,dev bash}' '1'
test_format '#{m/p:dev !ssh,dev bash}' '0,1,2'
test_format '#{m/z:dev !ssh,dev ssh}' '0'
test_format '#{m/p:dev !ssh,dev ssh}' ''
# OR separates groups; each group is an AND of its terms.
test_format '#{m/z:prod | dev,dev bash}' '1'
test_format '#{m/p:prod | dev,dev bash}' '0,1,2'
test_format '#{m/z:prod | dev,prod bash}' '1'
test_format '#{m/p:prod | dev,prod bash}' '0,1,2,3'
test_format '#{m/z:prod | dev,test bash}' '0'
test_format '#{m/p:prod | dev,test bash}' ''
# Style sequences do not count towards display positions.
test_format '#{m/z:dev,#[bold]dev#[default]}' '1'
test_format '#{m/p:dev,#[bold]dev#[default]}' '0,1,2'
test_format '#{m/p:dev,#[fg=red#,bg=blue]dev#[default]}' '0,1,2'
test_format '#{m/p:dev,#[bold]d#[default]e#[underscore]v#[default]}' '0,1,2'
test_format '#{m/p:bash,#[bold]dev#[default] bash}' '4,5,6,7'
# UTF-8. Non-ASCII matching is exact, even when ASCII would fold case.
test_format '#{m/z:é,café}' '1'
test_format '#{m/p:é,café}' '3'
test_format '#{m/z:é,É}' '0'
test_format '#{m/p:é,É}' ''
test_format '#{m/z:éx,éx}' '1'
test_format '#{m/p:éx,éx}' '0,1'
# Wide UTF-8 characters occupy two display cells and both should be marked for
# highlighting.
test_format '#{m/z:界,a界b}' '1'
test_format '#{m/p:界,a界b}' '1,2'
exit 0

300
regress/prompt-keys.sh Normal file
View File

@@ -0,0 +1,300 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
LC_ALL=C.UTF-8
LANG=C.UTF-8
export TERM LC_ALL LANG
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
OUT="$TEST_TMUX -Ltest -f/dev/null" # outer (host for the client)
IN="$TEST_TMUX -Ltest2 -f/dev/null" # inner (under test)
$OUT kill-server 2>/dev/null
$IN kill-server 2>/dev/null
trap "$OUT kill-server 2>/dev/null; $IN kill-server 2>/dev/null" EXIT
fail() {
echo "[FAIL] $1"
exit 1
}
# Capture the outer pane: what the inner client rendered. No -e, so we match
# plain visible text, not styles or escape sequences.
capture() {
$OUT capture-pane -p
}
# The "(search) ..." prompt row of a mode prompt.
search_row() {
capture | grep '(search)' | head -1
}
# The inner status line is the last row of the outer capture (status is at the
# bottom). Used to assert a prompt is in the pane and not on the status line.
status_line() {
capture | tail -1
}
# Inner mode must still be a tree mode (the prompt did not close or crash it).
in_tree_mode() {
[ "$($IN display-message -p '#{pane_mode}')" = "tree-mode" ]
}
# Small settle for the key -> inner server -> inner client -> outer pane round
# trip to redraw before we capture. Matches the short waits other regress tests
# use; we do not depend on exact timing beyond the redraw completing.
settle() {
sleep 0.5
}
# Assert the mode search prompt currently shows exactly "(search) <want>".
search_is() {
want=$1; msg=$2
search_row | grep -qF "(search) $want" || \
fail "$msg (wanted '(search) $want', got '$(search_row)')"
}
# --- Inner session under test. ---------------------------------------------
#
# Two windows so the tree has content; status on so we can distinguish the pane
# from the status line; fixed size and manual sizing so the layout is stable.
# A root-table key opens a status-line command prompt whose accept action writes
# the final buffer into @r, so we can recover it exactly.
$IN new -d -x80 -y24 "sh -c 'exec sleep 1000'" || exit 1
$IN set -g status on || exit 1
$IN set -g status-position bottom || exit 1
$IN set -g status-keys emacs || exit 1
$IN set -g window-size manual || exit 1
$IN new-window -d "sh -c 'exec sleep 1000'" || exit 1
$IN bind -n M-r command-prompt -p ">" "set -g @r '%%'" || exit 1
# --- Outer session: attach the inner one inside its pane. -------------------
$OUT new -d -x80 -y24 || exit 1
$OUT set -g status off || exit 1
$OUT set -g window-size manual || exit 1
$OUT send-keys -l "$IN attach" || exit 1
$OUT send-keys Enter || exit 1
sleep 1
# ===========================================================================
# Mode prompt (choose-tree search): the thorough engine vehicle.
# ===========================================================================
$IN choose-tree || exit 1
settle
in_tree_mode || fail "choose-tree did not enter tree-mode"
# --- 1. Search prompt is drawn in the pane, not on the status line. ---
$IN send-keys C-s || exit 1
settle
search_row | grep -q '(search)' || fail "search prompt not drawn in the pane"
status_line | grep -q '(search)' && \
fail "search prompt drawn on the status line, not in the pane"
# --- 2. emacs editing: insert and delete at middle, start and end. ---
# Cursor position is checked behaviourally: move, insert a marker, read the row.
# This needs no cursor coordinates and fails if a movement/edit key is wrong.
# Middle insert: "abcd", Left Left (cursor between b and c), insert X -> abXcd.
$IN send-keys -l "abcd" || exit 1
settle
search_is "abcd" "literal input not shown in search prompt"
$IN send-keys Left Left || exit 1
$IN send-keys -l "X" || exit 1
settle
search_is "abXcd" "middle insert wrong (Left/insert)"
# Middle delete: BSpace removes X (before cursor), DC removes c (at cursor).
$IN send-keys BSpace || exit 1
$IN send-keys DC || exit 1
settle
search_is "abd" "middle delete wrong (BSpace/Delete)"
# Clear, then start/end insert with C-a and C-e.
$IN send-keys C-u || exit 1
$IN send-keys -l "mno" || exit 1
$IN send-keys C-a || exit 1
$IN send-keys -l "S" || exit 1
$IN send-keys C-e || exit 1
$IN send-keys -l "E" || exit 1
settle
search_is "SmnoE" "C-a/C-e start/end insert wrong"
# Word kill: "hello world", C-w removes the last word leaving "hello " (with the
# separating space). capture-pane trims trailing spaces, so make the space
# visible by inserting a marker after it: the buffer becomes "hello Z".
$IN send-keys C-u || exit 1
$IN send-keys -l "hello world" || exit 1
$IN send-keys C-w || exit 1
$IN send-keys -l "Z" || exit 1
settle
search_is "hello Z" "C-w did not kill a word"
# C-a then C-k kills the whole line.
$IN send-keys C-a || exit 1
$IN send-keys C-k || exit 1
settle
search_row | grep -q '(search) [^ ]' && fail "C-a C-k did not clear the line"
# --- 3. Editing kept the prompt open the whole time. ---
in_tree_mode || fail "editing keys closed the mode"
search_row | grep -q '(search)' || fail "editing keys closed the prompt"
# --- 4. Unicode wide character: insert, render, delete as one unit. ---
$IN send-keys C-u || exit 1
$IN send-keys -l "a中b" || exit 1
settle
search_is "a中b" "wide character not shown"
# Left moves over "b" (one column); BSpace deletes the wide "中" as a single
# width-2 unit, leaving "ab".
$IN send-keys Left || exit 1
$IN send-keys BSpace || exit 1
settle
search_is "ab" "wide character not deleted as one unit"
# --- 5. Control character: quote-next inserts it literally, shown as ^G. ---
$IN send-keys C-u || exit 1
$IN send-keys -l "a" || exit 1
$IN send-keys C-v || exit 1 # quote next key
$IN send-keys C-g || exit 1 # literal BEL -> displayed as ^G
$IN send-keys -l "b" || exit 1
settle
search_is "a^Gb" "control character not shown as ^G"
# Deleted as a single unit too.
$IN send-keys Left || exit 1
$IN send-keys BSpace || exit 1
settle
search_is "ab" "control character not deleted as one unit"
# --- 6. Kill and yank: C-w fills the yank buffer, C-y pastes it at the cursor. ---
# (prompt_key only fills the yank buffer from C-w; C-y then yanks that text, or
# the top paste buffer if nothing has been killed. Establish our own kill here
# so the result is deterministic.)
$IN send-keys C-u || exit 1
$IN send-keys -l "one two" || exit 1
$IN send-keys C-w || exit 1 # kill "two", buffer "one "
$IN send-keys C-y || exit 1 # yank it back -> "one two"
settle
search_is "one two" "C-y did not yank the killed text"
$IN send-keys C-y || exit 1 # yank again at cursor -> "one twotwo"
settle
search_is "one twotwo" "second C-y did not yank again"
# --- 7. History: accept a string, reopen, Up recalls it. ---
$IN send-keys C-u || exit 1
$IN send-keys -l "alpha" || exit 1
$IN send-keys Enter || exit 1
settle
$IN send-keys C-s || exit 1
settle
$IN send-keys Up || exit 1
settle
search_is "alpha" "history (Up) did not recall the previous entry"
# --- 8. Escape closes the prompt but leaves the mode open. ---
$IN send-keys Escape || exit 1
settle
search_row | grep -q '(search)' && fail "Escape left a dangling search prompt"
in_tree_mode || fail "Escape closed the mode as well as the prompt"
# Leave the mode.
$IN send-keys q || exit 1
settle
# ===========================================================================
# Status-line prompt (command-prompt): the same engine on the status line.
# Keys go through the inner client's terminal (outer send-keys); the accepted
# buffer is recovered exactly via %% -> @r.
# ===========================================================================
# --- 10. Prompt is drawn on the status line (the last row). ---
$IN set -g @r "" || exit 1
$OUT send-keys M-r || exit 1
settle
status_line | grep -q '>' || fail "status-line prompt not drawn on the status line"
# --- 11. emacs cursor-marker edit, accept recovers the exact buffer. ---
$OUT send-keys -l "abc" || exit 1
$OUT send-keys Home || exit 1
$OUT send-keys -l "X" || exit 1
settle
status_line | grep -qF "> Xabc" || \
fail "status-line edit wrong (got '$(status_line)')"
$OUT send-keys Enter || exit 1
settle
[ "$($IN show -gv @r)" = "Xabc" ] || \
fail "status-line accept recovered '$($IN show -gv @r)', wanted 'Xabc'"
# --- 12. Unicode on the status line: insert, move, delete wide char. ---
$IN set -g @r "" || exit 1
$OUT send-keys M-r || exit 1
settle
$OUT send-keys -l "a㋡b" || exit 1
settle
status_line | grep -qF "a㋡b" || \
fail "status-line wide character not shown (got '$(status_line)')"
# Home, insert Z (start); End, BSpace (delete b); BSpace (delete wide char).
$OUT send-keys Home || exit 1
$OUT send-keys -l "Z" || exit 1
$OUT send-keys End || exit 1
$OUT send-keys BSpace || exit 1
$OUT send-keys BSpace || exit 1
settle
$OUT send-keys Enter || exit 1
settle
[ "$($IN show -gv @r)" = "Za" ] || \
fail "status-line wide edit recovered '$($IN show -gv @r)', wanted 'Za'"
# --- 13. Overflow: more text than fits stays within the line and is kept. ---
big="0123456789012345678901234567890123456789012345678901234567890123456789ABCDEFGHIJ"
$IN set -g @r "" || exit 1
$OUT send-keys M-r || exit 1
settle
$OUT send-keys -l "$big" || exit 1
settle
# The drawn status line must not exceed the client width (80): no wrap, no crash.
width=$(status_line | awk '{print length($0)}')
[ "$width" -le 80 ] || fail "overflowing prompt drew $width columns, wider than 80"
$OUT send-keys Enter || exit 1
settle
# The whole buffer was kept despite only part being visible.
[ "$($IN show -gv @r)" = "$big" ] || fail "overflowing prompt lost buffer content"
# --- 14. Escape closes the status-line prompt cleanly. ---
$IN set -g @r "SENTINEL" || exit 1
$OUT send-keys M-r || exit 1
settle
$OUT send-keys -l "discard" || exit 1
$OUT send-keys Escape || exit 1
settle
status_line | grep -q '> discard' && fail "Escape left a dangling status-line prompt"
[ "$($IN show -gv @r)" = "SENTINEL" ] || fail "Escape ran the prompt's accept action"
# ===========================================================================
# Two clients attached to the same window: a mode prompt must render on both.
# ===========================================================================
$OUT new-window || exit 1
$OUT set -g status off || exit 1
$OUT send-keys -l "$IN attach" || exit 1
$OUT send-keys Enter || exit 1
sleep 1
$IN choose-tree || exit 1
settle
$IN send-keys C-s || exit 1
settle
$IN send-keys -l "dual" || exit 1
settle
for w in $($OUT list-windows -F '#{window_index}'); do
$OUT capture-pane -t ":$w" -p | grep -qF "(search) dual" || \
fail "mode prompt not shown on client in outer window $w"
done
$IN send-keys Escape || exit 1
settle
# --- Inner tmux is still alive and responsive. ---
$IN display-message -p '#{version}' >/dev/null 2>&1 || fail "inner tmux died"
exit 0

336
regress/prompt-mechanics.sh Normal file
View File

@@ -0,0 +1,336 @@
#!/bin/sh
# Exercise the prompt mechanics shared by all three host paths of the prompt
# engine in prompt.c:
#
# status.c status_prompt_set - the status-line command prompt.
# window.c window_pane_set_prompt - a prompt drawn over a pane (-P).
# mode-tree.c mode_tree_set_prompt - search/filter prompts in tree modes.
#
# prompt-keys.sh covers the editing keys; this test covers that each path OPENS
# and DRAWS in the right place and that the prompt flags select the right engine
# behaviour: -1 (single), -N (numeric), -i (incremental), -k (key), -e
# (backspace exit), -I (prefill), multi prompts, type-scoped history and command
# completion.
PATH=/bin:/usr/bin
TERM=screen
LC_ALL=C.UTF-8
LANG=C.UTF-8
export TERM LC_ALL LANG
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
OUT="$TEST_TMUX -Ltest -f/dev/null" # outer (host for the client)
IN="$TEST_TMUX -Ltest2 -f/dev/null" # inner (under test)
$OUT kill-server 2>/dev/null
$IN kill-server 2>/dev/null
trap "$OUT kill-server 2>/dev/null; $IN kill-server 2>/dev/null" EXIT
fail() {
echo "[FAIL] $1"
exit 1
}
# Capture the outer pane: what the inner client rendered. Pane-area prompts
# (over a pane or in a tree mode) and the status-line prompt all end up here.
capture() {
$OUT capture-pane -p
}
# The inner status line is the last row of the outer capture.
status_line() {
capture | tail -1
}
# The recovered buffer. Every prompt below accepts into the @r option so the
# exact final string can be checked; reset it to a sentinel first so we can tell
# "accept ran" from "prompt cancelled".
got() {
$IN show -gv @r
}
reset() {
$IN set -g @r "SENTINEL" || exit 1
}
# Settle for the key -> inner server -> inner client -> outer pane redraw round
# trip, as in prompt-keys.sh.
settle() {
sleep 0.5
}
# --- Inner session under test. ---------------------------------------------
#
# The window is created at -y23 so that, with a one-row bottom status, the pane
# area (23) exactly fills the 24-row client: a pane prompt drawn on the pane's
# bottom row lands on visible row 23, just above the status line on row 24. A
# second, distinctively named window gives the tree something to filter.
$IN new -d -x80 -y23 -n YAK "sh -c 'exec sleep 1000'" || exit 1
$IN set -g status on || exit 1
$IN set -g status-position bottom || exit 1
$IN set -g status-keys emacs || exit 1
$IN set -g window-size manual || exit 1
$IN new-window -d -n ZEBRA "sh -c 'exec sleep 1000'" || exit 1
# One root-table key per path/flag. The accept template records the final
# buffer in @r (or both buffers, for the multi-prompt case).
$IN bind -n M-s command-prompt -p '(stat)' "set -g @r '%%'" || exit 1
$IN bind -n M-p command-prompt -P -p '(pane)' "set -g @r '%%'" || exit 1
$IN bind -n M-o command-prompt -P -1 -p '(one)' "set -g @r '%%'" || exit 1
$IN bind -n M-n command-prompt -P -N -I 5 -p '(num)' "set -g @r '%%'" || exit 1
$IN bind -n M-i command-prompt -P -i -p '(inc)' "set -g @r '%%'" || exit 1
$IN bind -n M-k command-prompt -P -k -p '(key)' "set -g @r '%%'" || exit 1
$IN bind -n M-e command-prompt -P -e -p '(bs)' "set -g @r '%%'" || exit 1
$IN bind -n M-j command-prompt -P -I hello -p '(pre)' "set -g @r '%%'" || exit 1
$IN bind -n M-m command-prompt -p 'first,second' "set -g @r '%1/%2'" || exit 1
$IN bind -n M-c command-prompt -p '(cmd)' "set -g @r '%%'" || exit 1
$IN bind -n M-h command-prompt -T search -p '(srch)' "set -g @r '%%'" || exit 1
# --- Outer session: attach the inner one inside its pane. -------------------
$OUT new -d -x80 -y24 || exit 1
$OUT set -g status off || exit 1
$OUT set -g window-size manual || exit 1
$OUT send-keys -l "$IN attach" || exit 1
$OUT send-keys Enter || exit 1
sleep 1
# ===========================================================================
# 1. Each path opens and draws in the right place.
# ===========================================================================
# --- 1a. status.c: drawn on the status line (the last row). ---
reset
$OUT send-keys M-s || exit 1
settle
status_line | grep -qF '(stat)' || \
fail "status-line prompt not on the status line (got '$(status_line)')"
$OUT send-keys -l "go" || exit 1
$OUT send-keys Enter || exit 1
settle
[ "$(got)" = "go" ] || fail "status-line accept recovered '$(got)', wanted 'go'"
# --- 1b. window.c: drawn over the pane, not on the status line. ---
reset
$OUT send-keys M-p || exit 1
settle
capture | grep -qF '(pane)' || fail "pane prompt not drawn in the pane"
status_line | grep -qF '(pane)' && \
fail "pane prompt drawn on the status line, not over the pane"
$OUT send-keys -l "deep" || exit 1
$OUT send-keys Enter || exit 1
settle
[ "$(got)" = "deep" ] || fail "pane prompt accept recovered '$(got)', wanted 'deep'"
# --- 1c. mode-tree.c: search prompt drawn in the pane. ---
$IN choose-tree || exit 1
settle
[ "$($IN display-message -p '#{pane_mode}')" = "tree-mode" ] || \
fail "choose-tree did not enter tree-mode"
$IN send-keys C-s || exit 1
settle
capture | grep -qF '(search)' || fail "mode-tree search prompt not drawn in the pane"
status_line | grep -qF '(search)' && \
fail "mode-tree search prompt drawn on the status line"
$IN send-keys Escape || exit 1
settle
# --- 1d. mode-tree.c: filter prompt opens, applies, prefills, and clears. ---
$IN send-keys f || exit 1
settle
capture | grep -qF '(filter)' || fail "mode-tree filter prompt not drawn"
$IN send-keys -l "ZEBRA" || exit 1
$IN send-keys Enter || exit 1
settle
capture | grep -q 'filter: active' || fail "accepting the filter did not apply it"
# Reopening the filter prompt prefills it with the current filter.
$IN send-keys f || exit 1
settle
capture | grep -qF '(filter) ZEBRA' || \
fail "filter prompt not prefilled with the current filter"
$IN send-keys Escape || exit 1
settle
# 'c' clears the filter.
$IN send-keys c || exit 1
settle
capture | grep -q 'filter: active' && fail "'c' did not clear the filter"
$IN send-keys q || exit 1
settle
# ===========================================================================
# 2. Flags select the right engine behaviour.
# ===========================================================================
# --- 2a. -1 (PROMPT_SINGLE): one keystroke closes and accepts that char. ---
reset
$OUT send-keys M-o || exit 1
settle
capture | grep -qF '(one)' || fail "single prompt did not open"
$OUT send-keys -l "q" || exit 1
settle
[ "$(got)" = "q" ] || fail "single prompt recovered '$(got)', wanted 'q'"
capture | grep -qF '(one)' && fail "single prompt stayed open after one key"
# --- 2b. -N (PROMPT_NUMERIC): prefilled, digits append, non-digit closes. ---
reset
$OUT send-keys M-n || exit 1
settle
capture | grep -qF '(num) 5' || fail "numeric prompt not prefilled with 5"
$OUT send-keys -l "7" || exit 1 # 57
$OUT send-keys Enter || exit 1 # Enter is a non-digit: close
settle
[ "$(got)" = "57" ] || fail "numeric accept recovered '$(got)', wanted '57'"
# A non-digit key closes the prompt with the existing buffer, dropping the key.
reset
$OUT send-keys M-n || exit 1
settle
$OUT send-keys -l "x" || exit 1
settle
[ "$(got)" = "5" ] || fail "numeric non-digit close recovered '$(got)', wanted '5'"
capture | grep -qF '(num)' && fail "numeric prompt stayed open after a non-digit"
# --- 2c. -i (PROMPT_INCREMENTAL): callback fires on every edit, stays open. ---
# The incremental code path prefixes the buffer with '=' (or +/-), so the '='
# proves the value came through the incremental callback, not a plain accept.
reset
$OUT send-keys M-i || exit 1
settle
[ "$(got)" = "=" ] || fail "incremental prompt did not fire on open (got '$(got)')"
$OUT send-keys -l "a" || exit 1
settle
[ "$(got)" = "=a" ] || fail "incremental did not fire after 'a' (got '$(got)')"
$OUT send-keys -l "b" || exit 1
settle
[ "$(got)" = "=ab" ] || fail "incremental did not fire after 'b' (got '$(got)')"
capture | grep -qF '(inc)' || fail "incremental prompt closed during editing"
$OUT send-keys Escape || exit 1
settle
capture | grep -qF '(inc)' && fail "Escape did not close the incremental prompt"
# --- 2d. -k (PROMPT_KEY): the next key closes and delivers its name. ---
reset
$OUT send-keys M-k || exit 1
settle
capture | grep -qF '(key)' || fail "key prompt did not open"
$OUT send-keys -l "z" || exit 1
settle
[ "$(got)" = "z" ] || fail "key prompt recovered '$(got)', wanted 'z'"
capture | grep -qF '(key)' && fail "key prompt stayed open after a key"
# --- 2e. -e (PROMPT_BSPACE_EXIT): backspace on empty cancels (no accept). ---
reset
$OUT send-keys M-e || exit 1
settle
$OUT send-keys BSpace || exit 1
settle
[ "$(got)" = "SENTINEL" ] || fail "backspace-exit ran the accept action (got '$(got)')"
capture | grep -qF '(bs)' && fail "backspace on empty did not close the prompt"
# --- 2f. -I (prefill): prompt opens with the given buffer. ---
reset
$OUT send-keys M-j || exit 1
settle
capture | grep -qF '(pre) hello' || fail "prefill not shown (got '$(capture | grep -F '(pre)')')"
$OUT send-keys Enter || exit 1
settle
[ "$(got)" = "hello" ] || fail "prefill accept recovered '$(got)', wanted 'hello'"
# --- 2g. Multi prompt: accept advances to the next, both are delivered. ---
reset
$OUT send-keys M-m || exit 1
settle
capture | grep -qF 'first' || fail "first of multi prompt not shown"
$OUT send-keys -l "X" || exit 1
$OUT send-keys Enter || exit 1
settle
capture | grep -qF 'second' || fail "multi prompt did not advance to the second"
$OUT send-keys -l "Y" || exit 1
$OUT send-keys Enter || exit 1
settle
[ "$(got)" = "X/Y" ] || fail "multi prompt recovered '$(got)', wanted 'X/Y'"
# ===========================================================================
# 3. Type-scoped history and command completion.
# ===========================================================================
# --- 3a. Command history is recalled with Up; search history is separate. ---
reset
$OUT send-keys M-c || exit 1
settle
$OUT send-keys -l "alpha" || exit 1
$OUT send-keys Enter || exit 1
settle
[ "$(got)" = "alpha" ] || fail "command prompt accept recovered '$(got)'"
# Reopen the command prompt: Up recalls the command-type entry.
$OUT send-keys M-c || exit 1
settle
$OUT send-keys Up || exit 1
settle
capture | grep -qF '(cmd) alpha' || fail "Up did not recall command history"
$OUT send-keys Escape || exit 1
settle
# A search-type prompt must NOT recall the command-type entry (separate rings).
$OUT send-keys M-h || exit 1
settle
$OUT send-keys Up || exit 1
settle
capture | grep -qF 'alpha' && fail "search prompt recalled command-type history"
$OUT send-keys Escape || exit 1
settle
# --- 3b. Tab completion works in a command prompt (command type only). ---
$OUT send-keys M-c || exit 1
settle
$OUT send-keys -l "new-w" || exit 1
$OUT send-keys Tab || exit 1
settle
status_line | grep -qF 'new-window' || \
fail "Tab did not complete new-w to new-window (got '$(status_line)')"
$OUT send-keys Escape || exit 1
settle
# A search-type prompt does not complete commands: Tab is literal / inert.
$OUT send-keys M-h || exit 1
settle
$OUT send-keys -l "new-w" || exit 1
$OUT send-keys Tab || exit 1
settle
status_line | grep -qF 'new-window' && fail "search prompt completed a command"
$OUT send-keys Escape || exit 1
settle
# ===========================================================================
# 4. Robustness: re-entrancy guard and Escape, then liveness.
# ===========================================================================
# --- 4a. A second prompt while one is open is refused (status path). ---
client=$($IN list-clients -F '#{client_name}' | head -1)
reset
$OUT send-keys M-s || exit 1
settle
$OUT send-keys -l "AAA" || exit 1
settle
$IN command-prompt -t"$client" -p '(re)' "set -g @r 'REENTERED'" 2>/dev/null
settle
capture | grep -qF '(re)' && fail "a second status prompt opened over the first"
status_line | grep -qF '(stat) AAA' || fail "first status prompt was disturbed"
$OUT send-keys Escape || exit 1
settle
[ "$(got)" = "SENTINEL" ] || fail "Escape ran the status prompt accept action"
# --- 4b. A second pane prompt while one is open is refused (pane path). ---
reset
$OUT send-keys M-p || exit 1
settle
$OUT send-keys -l "BBB" || exit 1
settle
$IN command-prompt -P -t"$client" -p '(re)' "set -g @r 'REENTERED'" 2>/dev/null
settle
capture | grep -qF '(re)' && fail "a second pane prompt opened over the first"
capture | grep -qF '(pane) BBB' || fail "first pane prompt was disturbed"
$OUT send-keys Escape || exit 1
settle
[ "$(got)" = "SENTINEL" ] || fail "Escape ran the pane prompt accept action"
# --- 4c. Inner tmux survived every path and flag. ---
$IN display-message -p '#{version}' >/dev/null 2>&1 || fail "inner tmux died"
exit 0

View File

@@ -1,12 +1,12 @@
│
┌────────────────┐
│ 
│ │
│ │
│ │
│ │
│ 
└────────────────┘
│
┌──────────────┐
│ │
│ │
│ │
│ │
└──────────────┘

│
│
│

View File

@@ -1,12 +1,12 @@
base
┌────────────────────┐
│FLOAT │
│ │
└────────────────────┘
┌──────────────────┐
│FLOAT
│ │
│ │
│ │
└──────────────────┘

View File

@@ -1,12 +1,12 @@
bas╔════════════════════════════╗
║FLOAT ║
║ ║
╚════════════════════════════╝
base
╔══════════════════════════╗
║FLOAT
║ ║
║ ║
║ ║
║ ║
║ ║
╚══════════════════════════╝

View File

@@ -6,7 +6,7 @@
06:abcdefghijklmnopqrstuvwxyz0123456789
07:abcdefghijklmnopqrstuvwxyz0123456789
08:abcdefghijklmnopqrstuvwxyz0123456789
09:abcdef┌────────────────────┐23456789
10:abcdef│BOTTOM │23456789
11:abcdef │23456789
09:abcdefghijklmnopqrstuvwxyz0123456789
10:abcdefg┌──────────────────┐123456789
11:abcdefg│BOTTOM123456789
│ │

View File

@@ -5,8 +5,8 @@ base
┌──────────
│CLIP
┌─────────
│CLIP

View File

@@ -1,12 +1,12 @@
01:abcdefghijklmnopqrstuvwxyz0123456789
02:abcdefghijklmnopqrstuvwxyz0123456789
──────────┐ijklmnopqrstuvwxyz0123456789
ijklmnopqrstuvwxyz0123456789
│ijklmnopqrstuvwxyz0123456789
│ijklmnopqrstuvwxyz0123456789
│ijklmnopqrstuvwxyz0123456789
│ijklmnopqrstuvwxyz0123456789
ijklmnopqrstuvwxyz0123456789
──────────┘ijklmnopqrstuvwxyz0123456789
03:abcdefghijklmnopqrstuvwxyz0123456789
─────────┐hijklmnopqrstuvwxyz0123456789
hijklmnopqrstuvwxyz0123456789
hijklmnopqrstuvwxyz0123456789
hijklmnopqrstuvwxyz0123456789
hijklmnopqrstuvwxyz0123456789
─────────┘hijklmnopqrstuvwxyz0123456789
10:abcdefghijklmnopqrstuvwxyz0123456789
11:abcdefghijklmnopqrstuvwxyz0123456789

View File

@@ -1,12 +1,12 @@
01:abcdefghijklmnopqrstuvwxyz0123456789
02:abcdefghijklmnopqrstuvwxyz0123456789
03:abcdefghijklmnopqrstuvwxyz┌──────────
04:abcdefghijklmnopqrstuvwxyz│RIGHT
05:abcdefghijklmnopqrstuvwxyz
06:abcdefghijklmnopqrstuvwxyz│
07:abcdefghijklmnopqrstuvwxyz│
08:abcdefghijklmnopqrstuvwxyz│
09:abcdefghijklmnopqrstuvwxyz
10:abcdefghijklmnopqrstuvwxyz└──────────
03:abcdefghijklmnopqrstuvwxyz0123456789
04:abcdefghijklmnopqrstuvwxyz0┌─────────
05:abcdefghijklmnopqrstuvwxyz0│RIGHT
06:abcdefghijklmnopqrstuvwxyz0
07:abcdefghijklmnopqrstuvwxyz0
08:abcdefghijklmnopqrstuvwxyz0
09:abcdefghijklmnopqrstuvwxyz0└─────────
10:abcdefghijklmnopqrstuvwxyz0123456789
11:abcdefghijklmnopqrstuvwxyz0123456789

View File

@@ -1,7 +1,7 @@
01:abcdef│ │23456789
02:abcdef│ │23456789
03:abcdef│ │23456789
04:abcdef└────────────────────┘23456789
01:abcdefg│ │123456789
02:abcdefg│ │123456789
03:abcdefg└──────────────────┘123456789
04:abcdefghijklmnopqrstuvwxyz0123456789
05:abcdefghijklmnopqrstuvwxyz0123456789
06:abcdefghijklmnopqrstuvwxyz0123456789
07:abcdefghijklmnopqrstuvwxyz0123456789

View File

@@ -1,8 +1,8 @@
──────────────┘
─────────────┘

View File

@@ -1,12 +1,12 @@
········································
········································
·······┌────────────────────┐···········
·······│FLOAT │···········
······· │···········
·······│ │···········
·······│ │···········
·······│ │···········
·······│ │···········
·······└────────────────────┘···········
········································
········┌──────────────────┐············
········│FLOAT │············
········│ │············
········│ │············
········│ │············
········└──────────────────┘············
········································
········································

View File

@@ -1,12 +1,12 @@
ba┌── one ─────────┐
│ONE │
│ │
└──────────────┏━━━━━━━━━━━━━━━━┓
┃TWO ┃
┃ ┃
┗━━ two ━━━━━━━━━┛
base
┌── one ───────┐
│ONE
│ │
└──────────────┘
┏━━━━━━━━━━━━━━┓
┃TWO
┃ ┃
┗━━ two ━━━━━━━┛

View File

@@ -1,12 +1,12 @@
base │···········
│···········
│···········
┌──────────│···········
│OUT │···········
│···········
│···········
│···········
───────────────────────────┘···········
│···········
┌─────────│···········
│OUT │···········
│ │···········
│ │···········
───────────────────────────┘···········
········································
········································
········································

View File

@@ -1,12 +1,12 @@
base 
 
 ┌────────────────┐
 │OVERSB  
 │  │
 │  │
 │  │
 │  │
 │  
 └────────────────┘
 
 ┌──────────────┐ 
 │OVERSB  │ 
 │  │ 
 │  │ 
 │  │ 
 └──────────────┘ 
 
 
 

View File

@@ -4,9 +4,9 @@ base
┌────────────────────┐
│OVERST │
┌──────────────────┐
│OVERST
│ │
│ │

View File

@@ -1,12 +1,12 @@
TILE01 abcdefghij │TILE01 abcdefghij
TILE02 abcdefghij │TILE02 abcdefghij
TILE03 abc╔════════════════╗bcdefghij
TILE04 abc║FLT ║bcdefghij
──────────║ ║────────────
TILE00 abc║ ║bcdefghij
TILE01 abc║ ║bcdefghij
TILE02 abc║ ║bcdefghij
TILE03 abc╚════════════════╝bcdefghij
TILE03 abcdefghij │TILE03 abcdefghij
TILE04 abcd╔══════════════╗abcdefghij
║FLT
──────────║ ║────────────
TILE00 abcd║ ║abcdefghij
TILE01 abcd║ ║abcdefghij
TILE02 abcd╚══════════════╝abcdefghij
TILE03 abcdefghij │TILE03 abcdefghij
TILE04 abcdefghij │TILE04 abcdefghij

View File

@@ -1,12 +1,12 @@
TILE01 abcdefghij │TILE01 abcdefghij
TILE02 abcdefghij │TILE02 abcdefghij
TILE03 abc┌────────────────┐bcdefghij
TILE04 abc│FLT │bcdefghij
──────────│ │────────────
TILE00 abc│ │bcdefghij
TILE01 abc│ │bcdefghij
TILE02 abc│ │bcdefghij
TILE03 abc└────────────────┘bcdefghij
TILE03 abcdefghij │TILE03 abcdefghij
TILE04 abcd┌──────────────┐abcdefghij
│FLT
──────────│ │────────────
TILE00 abcd│ │abcdefghij
TILE01 abcd│ │abcdefghij
TILE02 abcd└──────────────┘abcdefghij
TILE03 abcdefghij │TILE03 abcdefghij
TILE04 abcdefghij │TILE04 abcdefghij

View File

@@ -1,12 +1,12 @@
base
┌────────────────┐
│AAA │
┌────────────────┐
BBB
│ │
└─────────│
│ │
┌──────────────┐
│AAA
│ │
┌──────────────┐
└─────────│BBB
│ │
│ │
└──────────────┘

View File

@@ -1,12 +1,12 @@
── base ────────────────────────────────
base ┌── title ───────────┐
│FLOAT │
│ │
└────────────────────┘
base
┌── title ─────────┐
│FLOAT
│ │
│ │
│ │
└──────────────────┘

View File

@@ -1,12 +1,12 @@
┌────────────────────┐
│ │
│ │
│ │
│ │
│ │
│ │
└────────────────────┘
┌──────────────────┐
│ │
│ │
│ │
│ │
└──────────────────┘

View File

@@ -1,12 +1,12 @@
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│
│

View File

@@ -1,12 +1,12 @@
── 0:left ──────────┬── 1:right ────────
│
│
│
│
│
│
│
│
│
│
│
── 0:left ──────────┬── 1:right ────────
 │
│
│
│
│
│
│
│
│
│
│

View File

@@ -4,7 +4,7 @@
────────────────────────────────────────
────────────────────────────────────────

View File

@@ -1,12 +1,12 @@
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ 
│ 
│ 
│ 
│ 
│ 
│ 
│ 
│ 
│ 
│ 
│ 

View File

@@ -1,12 +1,12 @@
SB00 abcdefghij 
SB01 abcdefghij 
SB02 ab┌────────────────────┐ 
SB03 ab│FLOAT00 abcdef  │ 
SB04 ab│FLOAT01 abcdef  │ 
SB05 ab│FLOAT02 abcdef  │ 
SB06 ab│FLOAT03 abcdef  │ 
SB07 ab│FLOAT04 abcdef  │ 
SB08 ab│  │ 
SB09 ab└────────────────────┘ 
SB02 abcdefghij 
SB03 abc┌──────────────────┐ 
SB04 abc│FLOAT02 abcdef  │ 
SB05 abc│FLOAT03 abcdef  │ 
SB06 abc│FLOAT04 abcdef  │ 
SB07 abc│  │ 
SB08 abc└──────────────────┘ 
SB09 abcdefghij 
SB10 abcdefghij 
 

View File

@@ -1,12 +1,12 @@
 SB00 abcdefghij │ SBL00 abcdefghij
 SB01 abcdefghij │ SBL01 abcdefghij
 SB02 abcdefghij │ SBL02 abcdefghij
 SB03 abcdefghij │ SBL03 abcdefghij
 SB04 abcdefghij │ SBL04 abcdefghij
 SB05 abcdefghij │ SBL05 abcdefghij
 SB06 abcdefghij │ SBL06 abcdefghij
 SB07 abcdefghij │ SBL07 abcdefghij
 SB08 abcdefghij │ SBL08 abcdefghij
 SB09 abcdefghij │ SBL09 abcdefghij
 SB10 abcdefghij │ SBL10 abcdefghij
  │ 
 SB00 abcdefghij │ SBL00 abcdefghij
 SB01 abcdefghij │ SBL01 abcdefghij
 SB02 abcdefghij │ SBL02 abcdefghij
 SB03 abcdefghij │ SBL03 abcdefghij
 SB04 abcdefghij │ SBL04 abcdefghij
 SB05 abcdefghij │ SBL05 abcdefghij
 SB06 abcdefghij │ SBL06 abcdefghij
 SB07 abcdefghij  SBL07 abcdefghij
 SB08 abcdefghij  SBL08 abcdefghij
 SB09 abcdefghij  SBL09 abcdefghij
 SB10 abcdefghij  SBL10 abcdefghij
   

View File

@@ -1,12 +1,12 @@
SB00 abcdefghij  │SBR00 abcdefghij 
SB01 abcdefghij  │SBR01 abcdefghij 
SB02 abcdefghij  │SBR02 abcdefghij 
SB03 abcdefghij  │SBR03 abcdefghij 
SB04 abcdefghij  │SBR04 abcdefghij 
SB05 abcdefghij  │SBR05 abcdefghij 
SB06 abcdefghij  │SBR06 abcdefghij 
SB07 abcdefghij  │SBR07 abcdefghij 
SB08 abcdefghij  │SBR08 abcdefghij 
SB09 abcdefghij  │SBR09 abcdefghij 
SB10 abcdefghij  │SBR10 abcdefghij 
  │ 
SB00 abcdefghij  │SBR00 abcdefghij 
SB01 abcdefghij  │SBR01 abcdefghij 
SB02 abcdefghij  │SBR02 abcdefghij 
SB03 abcdefghij  │SBR03 abcdefghij 
SB04 abcdefghij  │SBR04 abcdefghij 
SB05 abcdefghij  │SBR05 abcdefghij 
SB06 abcdefghij  │SBR06 abcdefghij 
SB07 abcdefghij  │SBR07 abcdefghij 
SB08 abcdefghij  │SBR08 abcdefghij 
SB09 abcdefghij  │SBR09 abcdefghij 
SB10 abcdefghij  │SBR10 abcdefghij 
  │ 

View File

@@ -1,8 +1,8 @@
STYLE00 abcdefghij │STYLE00 abcdefghij
STYLE01 abcdefghij │STYLE01 abcdefghij
STYLE02 abcdefghij │STYLE02 abcdefghij
STYLE03 abcdefghij │STYLE03 abcdefghij
STYLE04 abcdefghij │STYLE04 abcdefghij
STYLE05 abcdefghij │STYLE05 abcdefghij
STYLE06 abcdefghij │STYLE06 abcdefghij
 │
STYLE00 abcdefghij │STYLE00 abcdefghij
STYLE01 abcdefghij │STYLE01 abcdefghij
STYLE02 abcdefghij │STYLE02 abcdefghij
STYLE03 abcdefghij │STYLE03 abcdefghij
STYLE04 abcdefghij │STYLE04 abcdefghij
STYLE05 abcdefghij │STYLE05 abcdefghij
STYLE06 abcdefghij │STYLE06 abcdefghij
 │

View File

@@ -24,7 +24,8 @@
#include "tmux.h"
static void
regsub_copy(char **buf, ssize_t *len, const char *text, size_t start, size_t end)
regsub_copy(char **buf, ssize_t *len, const char *text, size_t start,
size_t end)
{
size_t add = end - start;

File diff suppressed because it is too large Load Diff

View File

@@ -732,19 +732,19 @@ screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right,
gc.attr |= GRID_ATTR_CHARSET;
if (left)
screen_write_box_border_set(lines, CELL_LEFTJOIN, &gc);
screen_write_box_border_set(lines, CELL_URD, &gc);
else
screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
screen_write_box_border_set(lines, CELL_LR, &gc);
screen_write_cell(ctx, &gc);
screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
screen_write_box_border_set(lines, CELL_LR, &gc);
for (i = 1; i < nx - 1; i++)
screen_write_cell(ctx, &gc);
if (right)
screen_write_box_border_set(lines, CELL_RIGHTJOIN, &gc);
screen_write_box_border_set(lines, CELL_ULD, &gc);
else
screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
screen_write_box_border_set(lines, CELL_LR, &gc);
screen_write_cell(ctx, &gc);
screen_write_set_cursor(ctx, cx, cy);
@@ -752,7 +752,8 @@ screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right,
/* Draw a vertical line on screen. */
void
screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom)
screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom,
const struct grid_cell *gcp)
{
struct screen *s = ctx->s;
struct grid_cell gc;
@@ -761,7 +762,10 @@ screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom)
cx = s->cx;
cy = s->cy;
memcpy(&gc, &grid_default_cell, sizeof gc);
if (gcp != NULL)
memcpy(&gc, gcp, sizeof gc);
else
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.attr |= GRID_ATTR_CHARSET;
screen_write_putc(ctx, &gc, top ? 'w' : 'x');
@@ -847,26 +851,26 @@ screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny,
gc.flags |= GRID_FLAG_NOPALETTE;
/* Draw top border */
screen_write_box_border_set(lines, CELL_TOPLEFT, &gc);
screen_write_box_border_set(lines, CELL_RD, &gc);
screen_write_cell(ctx, &gc);
screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
screen_write_box_border_set(lines, CELL_LR, &gc);
for (i = 1; i < nx - 1; i++)
screen_write_cell(ctx, &gc);
screen_write_box_border_set(lines, CELL_TOPRIGHT, &gc);
screen_write_box_border_set(lines, CELL_LD, &gc);
screen_write_cell(ctx, &gc);
/* Draw bottom border */
screen_write_set_cursor(ctx, cx, cy + ny - 1);
screen_write_box_border_set(lines, CELL_BOTTOMLEFT, &gc);
screen_write_box_border_set(lines, CELL_RU, &gc);
screen_write_cell(ctx, &gc);
screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
screen_write_box_border_set(lines, CELL_LR, &gc);
for (i = 1; i < nx - 1; i++)
screen_write_cell(ctx, &gc);
screen_write_box_border_set(lines, CELL_BOTTOMRIGHT, &gc);
screen_write_box_border_set(lines, CELL_LU, &gc);
screen_write_cell(ctx, &gc);
/* Draw sides */
screen_write_box_border_set(lines, CELL_TOPBOTTOM, &gc);
screen_write_box_border_set(lines, CELL_UD, &gc);
for (i = 1; i < ny - 1; i++) {
/* left side */
screen_write_set_cursor(ctx, cx, cy + i);
@@ -1523,6 +1527,7 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
struct grid_line *gl;
u_int sx = screen_size_x(s);
struct screen_write_citem *ci = ctx->item;
u_int flags;
gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
@@ -1533,7 +1538,10 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
ctx->wp->flags |= PANE_REDRAW;
#endif
flags = gl->flags & (GRID_LINE_START_PROMPT|GRID_LINE_START_OUTPUT);
grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
gl->flags |= flags;
screen_write_collect_clear(ctx, s->cy, 1);
ci->x = 0;
@@ -2180,6 +2188,10 @@ screen_write_collect_flush_scrolled(struct screen_write_ctx *ctx)
screen_write_redraw_pane(ctx, &ttyctx);
return 0;
}
if (wp != NULL && window_pane_scrollbar_overlay_visible(wp)) {
wp->flags |= PANE_REDRAW;
return 0;
}
log_debug("%s: scrolled %u (region %u-%u)", __func__, ctx->scrolled,
s->rupper, s->rlower);
@@ -2193,7 +2205,7 @@ screen_write_collect_flush_scrolled(struct screen_write_ctx *ctx)
tty_write(tty_cmd_scrollup, &ttyctx);
if (wp != NULL)
wp->flags |= PANE_REDRAWSCROLLBAR;
window_pane_scrollbar_redraw(wp);
return 1;
}

View File

@@ -190,10 +190,11 @@ screen_reset_tabs(struct screen *s)
void
screen_set_default_cursor(struct screen *s, struct options *oo)
{
int c;
struct grid_cell gc;
int c;
c = options_get_number(oo, "cursor-colour");
s->default_ccolour = c;
style_apply(&gc, oo, "cursor-colour", NULL);
s->default_ccolour = gc.fg;
c = options_get_number(oo, "cursor-style");
s->default_mode = 0;
@@ -249,10 +250,7 @@ screen_set_title(struct screen *s, const char *title, int untrusted)
{
char *new_title;
if (untrusted)
new_title = clean_name(title, "#");
else
new_title = clean_name(title, "");
new_title = clean_name(title, untrusted);
if (new_title == NULL)
return (0);
free(s->title);
@@ -266,10 +264,7 @@ screen_set_path(struct screen *s, const char *path, int untrusted)
{
char *new_path;
if (untrusted)
new_path = clean_name(path, "#");
else
new_path = clean_name(path, "");
new_path = clean_name(path, untrusted);
if (new_path == NULL)
return (0);
free(s->path);

View File

@@ -48,6 +48,8 @@ static void server_client_dispatch(struct imsg *, void *);
static int server_client_dispatch_command(struct client *, struct imsg *);
static int server_client_dispatch_identify(struct client *, struct imsg *);
static int server_client_dispatch_shell(struct client *);
static void server_client_update_scrollbar_hover(struct client *, int, int,
int);
static void server_client_report_theme(struct client *, enum client_theme);
/* Compare client windows. */
@@ -287,6 +289,7 @@ struct client *
server_client_create(int fd)
{
struct client *c;
u_int i;
setblocking(fd, 0);
@@ -309,6 +312,9 @@ server_client_create(int fd)
c->tty.sx = 80;
c->tty.sy = 24;
for (i = 0; i < COLOUR_THEME_COUNT; i++)
c->theme_colours[i] = 8;
c->theme = THEME_UNKNOWN;
status_init(c);
@@ -360,6 +366,7 @@ server_client_open(struct client *c, char **cause)
if (tty_open(&c->tty, cause) != 0)
return (-1);
server_client_update_theme_colours(c);
return (0);
}
@@ -485,10 +492,7 @@ server_client_lost(struct client *c)
free(c->message_string);
if (event_initialized(&c->message_timer))
evtimer_del(&c->message_timer);
free(c->prompt_saved);
free(c->prompt_string);
free(c->prompt_buffer);
prompt_free(c->prompt);
format_lost_client(c);
environ_free(c->environ);
@@ -530,6 +534,7 @@ server_client_free(__unused int fd, __unused short events, void *arg)
log_debug("free client %p (%d references)", c, c->references);
redraw_free_scene(c->redraw_scene);
cmdq_free(c->queue);
if (c->references == 0) {
@@ -598,6 +603,58 @@ server_client_exec(struct client *c, const char *cmd)
free(msg);
}
/* Is this point inside the auto-hide scrollbar interaction area? */
static int
server_client_in_scrollbar_area(struct window_pane *wp, int px, int py)
{
struct window *w = wp->window;
u_int width, pad, total;
int start, end;
if (!window_pane_scrollbar_overlay(wp))
return (0);
if (py < wp->yoff || py >= wp->yoff + (int)wp->sy)
return (0);
width = wp->scrollbar_style.width;
pad = wp->scrollbar_style.pad;
total = width + pad;
if (total == 0 || total > wp->sx)
total = wp->sx;
if (w->sb_pos == PANE_SCROLLBARS_LEFT) {
start = wp->xoff;
end = wp->xoff + (int)total - 1;
} else {
end = wp->xoff + (int)wp->sx - 1;
start = end - (int)total + 1;
}
return (px >= start && px <= end);
}
/* Update auto-hide scrollbars for a mouse movement. */
static void
server_client_update_scrollbar_hover(struct client *c, int type, int px, int py)
{
struct window *w = c->session->curw->window;
struct window_pane *wp;
if (type != KEYC_TYPE_MOUSEMOVE)
return;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (!window_pane_is_visible(wp))
continue;
if (server_client_in_scrollbar_area(wp, px, py)) {
wp->sb_auto_hover = 1;
window_pane_scrollbar_show(wp, 1);
} else {
wp->sb_auto_hover = 0;
window_pane_scrollbar_start_timer(wp);
}
}
}
/* Is the mouse inside a pane? */
static enum key_code_mouse_location
server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
@@ -605,17 +662,19 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
{
struct window *w = wp->window;
struct window_pane *fwp;
int pane_status, sb, sb_pos, sb_w, sb_pad;
int pane_status, sb_w, sb_pad;
int pane_status_line, sl_top, sl_bottom;
int bdr_bottom, bdr_top, bdr_left, bdr_right;
int sb_start, sb_end, sb_overlay;
sb = options_get_number(w->options, "pane-scrollbars");
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
pane_status = window_pane_get_pane_status(wp);
sb_overlay = window_pane_scrollbar_overlay(wp);
if (window_pane_show_scrollbar(wp, sb)) {
if (window_pane_scrollbar_visible(wp)) {
sb_w = wp->scrollbar_style.width;
sb_pad = wp->scrollbar_style.pad;
if (sb_overlay && sb_w > (int)wp->sx)
sb_w = wp->sx;
} else {
sb_w = 0;
sb_pad = 0;
@@ -628,23 +687,48 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
else
pane_status_line = -1; /* not used */
bdr_left = wp->xoff - 1;
if (sb_pos == PANE_SCROLLBARS_LEFT)
if (!sb_overlay && w->sb_pos == PANE_SCROLLBARS_LEFT)
bdr_left -= sb_pad + sb_w;
if (sb_overlay && sb_w != 0 &&
py >= wp->yoff && py < wp->yoff + (int)wp->sy &&
px >= wp->xoff && px < wp->xoff + (int)wp->sx) {
if (w->sb_pos == PANE_SCROLLBARS_LEFT) {
sb_start = wp->xoff;
sb_end = sb_start + sb_w - 1;
} else {
sb_end = wp->xoff + (int)wp->sx - 1;
sb_start = sb_end - sb_w + 1;
}
if (px >= sb_start && px <= sb_end) {
sl_top = wp->yoff + wp->sb_slider_y;
sl_bottom = (wp->yoff + wp->sb_slider_y +
wp->sb_slider_h - 1);
if (py < sl_top)
return (KEYC_MOUSE_LOCATION_SCROLLBAR_UP);
else if (py >= sl_top && py <= sl_bottom) {
*sl_mpos = (py - wp->sb_slider_y - wp->yoff);
return (KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER);
} else
return (KEYC_MOUSE_LOCATION_SCROLLBAR_DOWN);
}
return (KEYC_MOUSE_LOCATION_PANE);
}
/* Check if point is within the pane or scrollbar. */
if (((pane_status != PANE_STATUS_OFF &&
py != pane_status_line && py != wp->yoff + (int)wp->sy) ||
(wp->yoff == 0 && py < (int)wp->sy) ||
(py >= wp->yoff && py < wp->yoff + (int)wp->sy)) &&
((sb_pos == PANE_SCROLLBARS_RIGHT &&
((w->sb_pos == PANE_SCROLLBARS_RIGHT &&
px < wp->xoff + (int)wp->sx + sb_pad + sb_w) ||
(sb_pos == PANE_SCROLLBARS_LEFT &&
(w->sb_pos == PANE_SCROLLBARS_LEFT &&
px < wp->xoff + (int)wp->sx - sb_pad - sb_w))) {
/* Check if in the scrollbar. */
if ((sb_pos == PANE_SCROLLBARS_RIGHT &&
if ((w->sb_pos == PANE_SCROLLBARS_RIGHT &&
(px >= wp->xoff + (int)wp->sx + sb_pad &&
px < wp->xoff + (int)wp->sx + sb_pad + sb_w)) ||
(sb_pos == PANE_SCROLLBARS_LEFT &&
(w->sb_pos == PANE_SCROLLBARS_LEFT &&
(px >= wp->xoff - sb_pad - sb_w &&
px < wp->xoff - sb_pad))) {
/* Check where inside the scrollbar. */
@@ -678,7 +762,7 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
if (window_pane_is_floating(fwp) &&
window_pane_get_pane_lines(fwp) == PANE_LINES_NONE)
continue;
if (window_pane_show_scrollbar(fwp, sb)) {
if (window_pane_scrollbar_reserve(fwp)) {
sb_w = fwp->scrollbar_style.width;
sb_pad = fwp->scrollbar_style.pad;
} else {
@@ -688,7 +772,7 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
bdr_top = fwp->yoff - 1;
bdr_bottom = fwp->yoff + fwp->sy;
bdr_left = fwp->xoff - 1;
if (sb_pos == PANE_SCROLLBARS_LEFT) {
if (w->sb_pos == PANE_SCROLLBARS_LEFT) {
bdr_left -= sb_pad + sb_w;
bdr_right = fwp->xoff + fwp->sx;
} else {
@@ -908,10 +992,14 @@ have_event:
tty_window_offset(&c->tty, &m->ox, &m->oy, &sx, &sy);
log_debug("mouse window @%u at %u,%u (%ux%u)",
w->id, m->ox, m->oy, sx, sy);
if (px > sx || py > sy)
if (px > sx || py > sy) {
server_client_update_scrollbar_hover(c, type,
-1, -1);
return (KEYC_UNKNOWN);
}
px = px + m->ox;
py = py + m->oy;
server_client_update_scrollbar_hover(c, type, px, py);
if (type == KEYC_TYPE_MOUSEDRAG && lwp != NULL) {
/* Use pane from last mouse event. */
@@ -944,7 +1032,8 @@ have_event:
m->wp = wp->id;
m->w = wp->window->id;
}
}
} else
server_client_update_scrollbar_hover(c, type, -1, -1);
/* Reset click type or add a click timer if needed. */
if (type == KEYC_TYPE_MOUSEDOWN ||
@@ -1079,6 +1168,52 @@ have_event:
return (key);
}
/* Update client theme colours from server options. */
void
server_client_update_theme_colours(struct client *c)
{
struct format_tree *ft;
const char *name, *value;
enum client_theme theme;
char *expanded;
u_int i;
int colour, option;
if (c == NULL)
return;
option = options_get_number(global_options, "theme");
if (option == 1) {
for (i = 0; i < COLOUR_THEME_COUNT; i++)
c->theme_colours[i] = colour_theme_terminal_colour(i);
return;
}
ft = format_create(c, NULL, FORMAT_NONE, FORMAT_NOJOBS);
format_defaults(ft, c, NULL, NULL, NULL);
theme = c->theme;
if (option == 2)
theme = THEME_LIGHT;
else if (option == 3)
theme = THEME_DARK;
for (i = 0; i < COLOUR_THEME_COUNT; i++) {
c->theme_colours[i] = 8;
name = colour_theme_option(i, theme);
if (name == NULL)
continue;
value = options_get_string(global_options, name);
expanded = format_expand(ft, value);
colour = colour_fromstring(expanded);
free(expanded);
if (colour == -1 || (colour & COLOUR_FLAG_THEME))
continue;
c->theme_colours[i] = colour;
}
format_free(ft);
}
/* Is this a bracket paste key? */
static int
server_client_is_bracket_paste(struct client *c, key_code key)
@@ -1459,6 +1594,7 @@ server_client_handle_key0(struct client *c, struct key_event *event,
{
struct session *s = c->session;
struct cmdq_item *item;
struct window_pane *wp;
/* Check the client is good to accept input. */
if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
@@ -1488,6 +1624,7 @@ server_client_handle_key0(struct client *c, struct key_event *event,
return (0);
status_message_clear(c);
}
if (c->overlay_key != NULL) {
switch (c->overlay_key(c, c->overlay_data, event)) {
case 0:
@@ -1497,10 +1634,40 @@ server_client_handle_key0(struct client *c, struct key_event *event,
return (0);
}
}
server_client_clear_overlay(c);
if (c->prompt_string != NULL) {
if (status_prompt_key(c, event->key) == 0)
if (c->prompt != NULL) {
switch (status_prompt_key(c, event->key, &event->m)) {
case PROMPT_KEY_HANDLED:
case PROMPT_KEY_CLOSE:
return (0);
case PROMPT_KEY_NOT_HANDLED:
case PROMPT_KEY_MOVE:
break;
}
}
wp = s->curw->window->active;
if (wp == NULL || !window_pane_has_prompt(wp)) {
TAILQ_FOREACH(wp, &s->curw->window->panes, entry) {
if (window_pane_has_prompt(wp) &&
window_pane_is_visible(wp))
break;
}
}
if (wp != NULL &&
window_pane_has_prompt(wp) &&
window_pane_is_visible(wp)) {
switch (window_pane_prompt_key(wp, c, event->key, &event->m)) {
case PROMPT_KEY_HANDLED:
case PROMPT_KEY_CLOSE:
case PROMPT_KEY_MOVE:
return (0);
case PROMPT_KEY_NOT_HANDLED:
if (KEYC_IS_MOUSE(event->key))
return (0);
break;
}
}
}
@@ -1773,6 +1940,42 @@ out:
bufferevent_enable(wp->event, EV_READ);
}
/* Move cursor for pane prompt. */
static int
server_client_prompt_cursor(struct client *c, struct window_pane *wp, int *mode,
u_int *cx, u_int *cy)
{
struct tty *tty = &c->tty;
struct visible_ranges *r;
u_int ox, oy, sx, sy;
int px, py;
if (!window_pane_has_prompt(wp))
return (0);
*mode &= ~MODE_CURSOR;
tty_window_offset(tty, &ox, &oy, &sx, &sy);
if (status_at_line(c) == 0)
py = wp->yoff;
else
py = wp->yoff + wp->sy - 1;
px = wp->xoff + wp->prompt_cx;
if (px < (int)ox || px > (int)(ox + sx) ||
py < (int)oy || py > (int)(oy + sy))
return (1);
*cx = px - ox;
*cy = py - oy;
r = window_visible_ranges(wp, *cx, *cy, 1, NULL);
if (window_position_is_visible(r, *cx)) {
if (status_at_line(c) == 0)
*cy += status_line_size(c);
*mode |= MODE_CURSOR;
}
return (1);
}
/*
* Update cursor position and mode settings. The scroll region and attributes
* are cleared when idle (waiting for an event) as this is the most likely time
@@ -1791,7 +1994,8 @@ server_client_reset_state(struct client *c)
struct screen *s = NULL;
struct options *oo = c->session->options;
int mode = 0, cursor, flags, pane_mode = 0;
u_int cx = 0, cy = 0, ox, oy, sx, sy, n;
u_int cx = 0, cy = 0, ox, oy, sx, sy, prompt = 0;
u_int sb_w;
struct visible_ranges *r;
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
@@ -1805,7 +2009,7 @@ server_client_reset_state(struct client *c)
if (c->overlay_draw != NULL) {
if (c->overlay_mode != NULL)
s = c->overlay_mode(c, c->overlay_data, &cx, &cy);
} else if (wp != NULL && c->prompt_string == NULL)
} else if (wp != NULL && c->prompt == NULL)
s = wp->screen;
else
s = c->status.active;
@@ -1821,42 +2025,50 @@ server_client_reset_state(struct client *c)
tty_margin_off(tty);
/* Move cursor to pane cursor and offset. */
if (c->prompt_string != NULL) {
n = options_get_number(oo, "status-position");
if (n == 0)
cy = status_prompt_line_at(c);
else {
n = status_line_size(c) - status_prompt_line_at(c);
if (n <= tty->sy)
cy = tty->sy - n;
else
cy = tty->sy - 1;
}
cx = c->prompt_cursor;
if (c->prompt != NULL) {
prompt = 1;
status_prompt_cursor(c, &cx, &cy);
} else if (wp != NULL && c->overlay_draw == NULL) {
cursor = 0;
pane_mode = wp->base.mode;
prompt = server_client_prompt_cursor(c, wp, &mode, &cx, &cy);
if (!prompt) {
cursor = 0;
pane_mode = wp->base.mode;
tty_window_offset(tty, &ox, &oy, &sx, &sy);
if (wp->xoff + (int)s->cx >= (int)ox &&
wp->xoff + (int)s->cx <= (int)ox + (int)sx &&
wp->yoff + (int)s->cy >= (int)oy &&
wp->yoff + (int)s->cy <= (int)oy + (int)sy) {
cursor = 1;
tty_window_offset(tty, &ox, &oy, &sx, &sy);
if (wp->xoff + (int)s->cx >= (int)ox &&
wp->xoff + (int)s->cx <= (int)ox + (int)sx &&
wp->yoff + (int)s->cy >= (int)oy &&
wp->yoff + (int)s->cy <= (int)oy + (int)sy) {
cursor = 1;
cx = wp->xoff + (int)s->cx - (int)ox;
cy = wp->yoff + (int)s->cy - (int)oy;
cx = wp->xoff + (int)s->cx - (int)ox;
cy = wp->yoff + (int)s->cy - (int)oy;
r = window_visible_ranges(wp, cx, cy, 1, NULL);
if (!window_position_is_visible(r, cx))
cursor = 0;
r = window_visible_ranges(wp, cx, cy, 1, NULL);
if (!window_position_is_visible(r, cx))
cursor = 0;
if (status_at_line(c) == 0)
cy += status_line_size(c);
if (window_pane_scrollbar_overlay_visible(wp)) {
sb_w = wp->scrollbar_style.width;
if (sb_w > wp->sx)
sb_w = wp->sx;
if (sb_w != 0 &&
w->sb_pos ==
PANE_SCROLLBARS_LEFT) {
if (s->cx < sb_w)
cursor = 0;
} else if (sb_w != 0 &&
s->cx >= wp->sx - sb_w)
cursor = 0;
}
if (status_at_line(c) == 0)
cy += status_line_size(c);
}
if ((pane_mode & MODE_SYNC) || !cursor)
mode &= ~MODE_CURSOR;
}
if ((pane_mode & MODE_SYNC) || !cursor)
mode &= ~MODE_CURSOR;
} else if (c->overlay_mode == NULL || s == NULL)
mode &= ~MODE_CURSOR;
if (~pane_mode & MODE_SYNC) {
@@ -1877,14 +2089,16 @@ server_client_reset_state(struct client *c)
mode |= MODE_MOUSE_ALL;
}
}
if (options_get_number(oo, "focus-follows-mouse"))
mode |= MODE_MOUSE_ALL;
if (options_get_number(oo, "focus-follows-mouse") ||
w->sb == PANE_SCROLLBARS_MODAL ||
w->sb == PANE_SCROLLBARS_AUTOHIDE)
mode |= MODE_MOUSE_ALL;
else if (~mode & MODE_MOUSE_ALL)
mode |= MODE_MOUSE_BUTTON;
}
/* Clear bracketed paste mode if at the prompt. */
if (c->overlay_draw == NULL && c->prompt_string != NULL)
if (c->overlay_draw == NULL && prompt)
mode &= ~MODE_BRACKETPASTE;
/* Set the terminal mode and reset attributes. */
@@ -2101,11 +2315,11 @@ server_client_check_redraw(struct client *c)
if (wp->flags & PANE_REDRAW) {
log_debug("%s: redraw pane %%%u", __func__,
wp->id);
screen_redraw_pane(c, wp, 0);
redraw_pane(c, wp);
} else if (wp->flags & PANE_REDRAWSCROLLBAR) {
log_debug("%s: redraw scrollbar %%%u", __func__,
wp->id);
screen_redraw_pane(c, wp, 1);
redraw_pane_scrollbar(c, wp);
}
}
}
@@ -2120,7 +2334,7 @@ server_client_check_redraw(struct client *c)
server_client_set_path(c);
}
server_client_set_progress_bar(c);
screen_redraw_screen(c);
redraw_screen(c);
}
/* Put the tty back how it was. */
@@ -2869,6 +3083,8 @@ out:
static void
server_client_report_theme(struct client *c, enum client_theme theme)
{
enum client_theme old = c->theme;
if (theme == THEME_LIGHT) {
c->theme = THEME_LIGHT;
notify_client("client-light-theme", c);
@@ -2877,6 +3093,17 @@ server_client_report_theme(struct client *c, enum client_theme theme)
notify_client("client-dark-theme", c);
}
/*
* If the theme has changed, update the theme colours and redraw the
* client.
*/
if (c->theme != old) {
server_client_update_theme_colours(c);
if (c->tty.flags & TTY_OPENED)
tty_invalidate(&c->tty);
server_redraw_client(c);
}
/*
* Request foreground and background colour again. Don't forward 2031 to
* panes until a response is received.

View File

@@ -254,7 +254,7 @@ server_start(struct tmuxproc *client, uint64_t flags, struct event_base *base,
proc_loop(server_proc, server_loop);
job_kill_all();
status_prompt_save_history();
prompt_save_history();
exit(0);
}

4
sort.c
View File

@@ -26,8 +26,8 @@
static struct sort_criteria *sort_criteria;
static void
sort_qsort(void *l, u_int len, u_int size, int (*cmp)(const void *, const void *),
struct sort_criteria *sort_crit)
sort_qsort(void *l, u_int len, u_int size, int (*cmp)(const void *,
const void *), struct sort_criteria *sort_crit)
{
u_int i;
void *tmp, **ll;

1818
status.c

File diff suppressed because it is too large Load Diff

110
style.c
View File

@@ -42,9 +42,17 @@ static struct style style_default = {
STYLE_WIDTH_DEFAULT, 0, STYLE_PAD_DEFAULT,
STYLE_DEFAULT_BASE
STYLE_DEFAULT_BASE,
0
};
/*
* Global hyperlink set holding the URIs for #[link=...] styles, so a style
* only needs to store a small ID rather than the URI itself.
*/
static struct hyperlinks *style_hyperlinks;
/* Set range string. */
static void
style_set_range_string(struct style *sy, const char *s)
@@ -91,6 +99,7 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
sy->gc.us = base->us;
sy->gc.attr = base->attr;
sy->gc.flags = base->flags;
sy->link = 0;
} else if (strcasecmp(tmp, "ignore") == 0)
sy->ignore = 1;
else if (strcasecmp(tmp, "noignore") == 0)
@@ -234,7 +243,9 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
} else if (strcasecmp(tmp, "none") == 0)
sy->gc.attr = 0;
else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) {
if (strcmp(tmp + 2, "attr") == 0)
if (strcmp(tmp + 2, "link") == 0)
sy->link = 0;
else if (strcmp(tmp + 2, "attr") == 0)
sy->gc.attr |= GRID_ATTR_NOATTR;
else {
value = attributes_fromstring(tmp + 2);
@@ -262,6 +273,15 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
if (errstr != NULL)
goto error;
sy->pad = (int)n;
} else if (strncasecmp(tmp, "link=", 5) == 0) {
if (tmp[5] == '\0')
sy->link = 0;
else {
if (style_hyperlinks == NULL)
style_hyperlinks = hyperlinks_init();
sy->link = hyperlinks_put(style_hyperlinks,
tmp + 5, tmp + 5);
}
} else {
if ((value = attributes_fromstring(tmp)) == -1)
goto error;
@@ -284,8 +304,8 @@ style_tostring(struct style *sy)
{
struct grid_cell *gc = &sy->gc;
int off = 0;
const char *comma = "", *tmp = "";
static char s[256];
const char *comma = "", *tmp = "", *uri;
static char s[2048];
char b[21];
*s = '\0';
@@ -389,15 +409,33 @@ style_tostring(struct style *sy)
comma = ",";
}
if (sy->pad >= 0) {
xsnprintf(s + off, sizeof s - off, "%spad=%u", comma,
off += xsnprintf(s + off, sizeof s - off, "%spad=%u", comma,
sy->pad);
comma = ",";
}
uri = style_link(sy);
if (uri != NULL) {
xsnprintf(s + off, sizeof s - off, "%slink=%s", comma, uri);
comma = ",";
}
if (*s == '\0')
return ("default");
return (s);
}
/* Get the hyperlink URI for a style, or NULL if it has none. */
const char *
style_link(struct style *sy)
{
const char *uri;
if (sy->link == 0 || style_hyperlinks == NULL)
return (NULL);
if (!hyperlinks_get(style_hyperlinks, sy->link, &uri, NULL, NULL))
return (NULL);
return (uri);
}
/* Apply a style on top of the given style. */
struct style *
style_add(struct grid_cell *gc, struct options *oo, const char *name,
@@ -434,6 +472,29 @@ style_apply(struct grid_cell *gc, struct options *oo, const char *name,
style_add(gc, oo, name, ft);
}
/* Parse a single colour into a style */
int
style_parse_colour(struct style *sy, const struct grid_cell *base,
const char *s)
{
int c;
style_set(sy, base);
if (*s == '\0') {
sy->gc.fg = -1;
return (0);
}
if ((c = colour_fromstring(s)) == -1)
return (-1);
if (c == 8)
sy->gc.fg = base->fg;
else
sy->gc.fg = c;
return (0);
}
/* Initialize style from cell. */
void
style_set(struct style *sy, const struct grid_cell *gc)
@@ -454,22 +515,33 @@ void
style_set_scrollbar_style_from_option(struct style *sb_style,
struct options *oo)
{
struct style *sy;
const struct options_table_entry *oe;
struct options_entry *o;
const char *s;
char *style, *expanded;
sy = options_string_to_style(oo, "pane-scrollbars-style", NULL);
if (sy == NULL) {
style_set(sb_style, &grid_default_cell);
sb_style->width = PANE_SCROLLBARS_DEFAULT_WIDTH;
sb_style->pad = PANE_SCROLLBARS_DEFAULT_PADDING;
utf8_set(&sb_style->gc.data, PANE_SCROLLBARS_CHARACTER);
} else {
style_copy(sb_style, sy);
if (sb_style->width < 1)
sb_style->width = PANE_SCROLLBARS_DEFAULT_WIDTH;
if (sb_style->pad < 0)
sb_style->pad = PANE_SCROLLBARS_DEFAULT_PADDING;
utf8_set(&sb_style->gc.data, PANE_SCROLLBARS_CHARACTER);
style_set(sb_style, &grid_default_cell);
o = options_get(oo, "pane-scrollbars-style");
if (o == NULL)
fatalx("missing pane-scrollbars-style");
oe = options_table_entry(o);
style = format_single(NULL, oe->default_str, NULL, NULL, NULL, NULL);
if (style_parse(sb_style, &grid_default_cell, style) != 0)
fatalx("bad pane-scrollbars-style default");
s = options_get_string(oo, "pane-scrollbars-style");
if (s != NULL) {
expanded = format_single(NULL, s, NULL, NULL, NULL, NULL);
if (style_parse(sb_style, &grid_default_cell, expanded) != 0)
style_parse(sb_style, &grid_default_cell, style);
free(expanded);
}
free(style);
if (sb_style->width < 1)
sb_style->width = PANE_SCROLLBARS_DEFAULT_WIDTH;
if (sb_style->pad < 0)
sb_style->pad = PANE_SCROLLBARS_DEFAULT_PADDING;
utf8_set(&sb_style->gc.data, PANE_SCROLLBARS_CHARACTER);
}
/* Initialize style ranges. */

449
tmux.1
View File

@@ -364,6 +364,8 @@ Choose the current window interactively.
Kill the current pane.
.It z
Toggle zoom state of the current pane.
.It Tab
Choose a new window and session by fuzzy matching.
.It {
Move floating pane to top-left corner.
.It }
@@ -2744,6 +2746,15 @@ and
.Fl Y
options set the position of the upper-left corner of the pane.
If omitted, new floating panes are cascaded from the top-left of the window.
The
.Fl x ,
.Fl y ,
.Fl X ,
and
.Fl Y
options may be followed by
.Ql %
to specify a percentage of the window size.
.Pp
If the pane had previously been floating, the position and sizes are restored
from the saved values not specified by the
@@ -2873,9 +2884,11 @@ The following keys may be used in client mode:
.It Li "z" Ta "Suspend selected client"
.It Li "Z" Ta "Suspend tagged clients"
.It Li "f" Ta "Enter a format to filter items"
.It Li "c" Ta "Clear the current filter"
.It Li "O" Ta "Change sort order"
.It Li "r" Ta "Reverse sort order"
.It Li "v" Ta "Toggle preview"
.It Li "i" Ta "Change view (preview and client information)"
.It Li "F1 or C\-h" Ta "Display help"
.It Li "q" Ta "Exit mode"
.El
@@ -2968,11 +2981,12 @@ The following keys may be used in tree mode:
.It Li "C\-t" Ta "Tag all items"
.It Li "\&:" Ta "Run a command for each tagged item"
.It Li "f" Ta "Enter a format to filter items"
.It Li "c" Ta "Clear the current filter"
.It Li "H" Ta "Jump to the starting pane"
.It Li "O" Ta "Change sort order"
.It Li "r" Ta "Reverse sort order"
.It Li "v" Ta "Toggle preview"
.It Li "i" Ta "Change view (preview and client information)"
.It Li "i" Ta "Change view (preview and session, window and pane information)"
.It Li "F1 or C\-h" Ta "Display help"
.It Li "q" Ta "Exit mode"
.El
@@ -3066,6 +3080,7 @@ The following keys may be used in customize mode:
.It Li "T" Ta "Tag no items"
.It Li "C\-t" Ta "Tag all items"
.It Li "f" Ta "Enter a format to filter items"
.It Li "c" Ta "Clear the current filter"
.It Li "v" Ta "Toggle option information"
.It Li "F1 or C\-h" Ta "Display help"
.It Li "q" Ta "Exit mode"
@@ -3087,6 +3102,51 @@ The
.Ic customize-mode
command works only if at least one client is attached.
.It Xo
.Ic switch\-mode
.Op Fl kswZ
.Op Fl F Ar format
.Op Fl t Ar target\-pane
.Op Ar command
.Xc
Put a pane into switch mode, where a session or window may be chosen
interactively from a list.
Each session or window is shown on one line and the list is narrowed by typing:
the typed text is matched against each item with fuzzy
matching and only matching items are shown, sorted by how well they match.
.Fl s
lists sessions (the default) and
.Fl w
lists windows.
.Fl Z
zooms the pane.
The following keys may be used in switch mode:
.Bl -column "KeyXXX" "Function" -offset indent
.It Sy "Key" Ta Sy "Function"
.It Li "Enter" Ta "Choose the selected item"
.It Li "Up" Ta "Select the previous item"
.It Li "Down" Ta "Select the next item"
.It Li "Escape" Ta "Exit mode"
.El
.Pp
After a session or window is chosen, the first instance of
.Ql %%
and all instances of
.Ql %1
are replaced by the target in
.Ar command
and the result executed as a command.
If
.Ar command
is not given, "switch\-client \-Zt \[aq]%%\[aq]" is used.
.Fl F
specifies the format for each item in the list.
.Fl k
kills the pane when the mode is exited.
.Pp
The appearance of matched characters is controlled by the
.Ic switch\-mode\-match\-style
option.
.It Xo
.Tg displayp
.Ic display\-panes
.Op Fl bN
@@ -3571,7 +3631,7 @@ but a different format may be specified with
.Fl F .
.Tg newp
.It Xo Ic new\-pane
.Op Fl bdefhIkPvWZ
.Op Fl bdefhIkLPvWZ
.Op Fl B Ar border\-lines
.Op Fl c Ar start\-directory
.Op Fl e Ar environment
@@ -3584,101 +3644,49 @@ but a different format may be specified with
.Op Fl S Ar active\-border\-style
.Op Fl t Ar target\-pane
.Op Fl T Ar title
.Op Fl x Ar width
.Op Fl y Ar height
.Op Fl X Ar x-position
.Op Fl Y Ar y-position
.Op Ar shell\-command Op Ar argument ...
.Xc
.D1 Pq alias: Ic newp
Create a new pane.
The new pane is created by splitting
.Ar target\-pane .
If
.Fl d
is given, the session does not make the new pane the current pane.
.Fl Z
zooms if the window is not zoomed, or keeps it zoomed if already zoomed.
.Fl s
sets the style for the pane content.
.Fl S
sets the border style when the pane is active and
.Fl R
sets the border style when the pane is inactive (see
.Sx STYLES ) .
.Fl T
sets the pane title.
Creates a new floating pane.
The
.Fl x
and
.Fl y
options set the width and height of the floating pane in columns and lines
respectively.
The default is half the window width and a quarter the window height.
The
.Fl X
and
.Fl Y
options set the position of the upper-left corner of the pane.
If omitted, new floating panes are cascaded from the top-left of the window.
The
.Fl x ,
.Fl y ,
.Fl X ,
and
.Fl Y
options may be followed by
.Ql %
to specify a percentage of the window size.
.Fl B
sets the pane border lines for floating panes; see
.Ic pane\-border\-lines .
.Pp
.Fl h
does a horizontal split and
.Fl v
a vertical split; if neither is specified,
.Fl v
is assumed.
The
.Fl l
option specifies the size of the new pane in lines (for vertical split) or in
columns (for horizontal split);
.Ar size
may be followed by
.Ql %
to specify a percentage of the available space.
.Fl p
is a shorthand option for this.
The
.Fl b
option causes the new pane to be created to the left of or above
.Ar target\-pane .
The
.Fl f
option creates a new pane spanning the full window height (with
.Fl h )
or full window width (with
.Fl v ) ,
instead of splitting the active pane.
.Pp
.Fl k
keeps the pane open after the optional
.Ar shell\-command
exits and waits for a key to be pressed before closing it.
The message shown is controlled by the
.Ic remain\-on\-exit\-format
option.
.Fl m Ar message
is equivalent to
.Fl k
but also sets the
.Ic remain\-on\-exit\-format
option for this pane to
.Ar message .
.Pp
.Fl W
Waits until
.Ar shell\-command
exits, then returns its exit status.
For example:
.Bd -literal -offset indent
$ tmux new-pane -W 'vi afile'
$ echo $?
0
.Ed
.Pp
.Fl E ,
or an empty
.Ar shell\-command ,
(\[aq]\[aq]) will create an empty pane with no command running in it;
.Ic display-message
.Fl I
can write to an empty pane.
The
.Fl I
flag will create an empty pane and forward any output from stdin to it.
For example:
.Bd -literal -offset indent
$ make 2>&1|tmux splitw \-dI &
.Ed
.Fl L
option makes
.Ic new\-pane
behave like
.Ic split\-window .
.Pp
All other options have the same meaning as for the
.Ic new\-window
.Ic split\-window
command.
.Tg nextl
.It Ic next\-layout Op Fl t Ar target\-window
@@ -4033,14 +4041,92 @@ the command behaves like
.Op Ar shell\-command Op Ar argument ...
.Xc
.D1 Pq alias: Ic splitw
Creates a new pane by splitting
Create a new pane by splitting
.Ar target\-pane .
Shares behavior with
.Ic new\-pane .
If
.Fl d
is given, the session does not make the new pane the current pane.
.Fl Z
zooms if the window is not zoomed, or keeps it zoomed if already zoomed.
.Fl s
sets the style for the pane content.
.Fl S
sets the border style when the pane is active and
.Fl R
sets the border style when the pane is inactive (see
.Sx STYLES ) .
.Fl T
sets the pane title.
.Pp
.Fl h
does a horizontal split and
.Fl v
a vertical split; if neither is specified,
.Fl v
is assumed.
The
.Fl l
option specifies the size of the new pane in lines (for vertical split) or in
columns (for horizontal split);
.Ar size
may be followed by
.Ql %
to specify a percentage of the available space.
.Fl p
is a shorthand option for this.
The
.Fl b
option causes the new pane to be created to the left of or above
.Ar target\-pane .
The
.Fl f
option creates a new pane spanning the full window height (with
.Fl h )
or full window width (with
.Fl v ) ,
instead of splitting the active pane.
.Pp
.Fl k
keeps the pane open after the optional
.Ar shell\-command
exits and waits for a key to be pressed before closing it.
The message shown is controlled by the
.Ic remain\-on\-exit\-format
option.
.Fl m Ar message
is equivalent to
.Fl k
but also sets the
.Ic remain\-on\-exit\-format
option for this pane to
.Ar message .
.Pp
.Fl W
Waits until
.Ar shell\-command
exits, then returns its exit status.
For example:
.Bd -literal -offset indent
$ tmux new-pane -W 'vi afile'
$ echo $?
0
.Ed
.Pp
.Fl E ,
or an empty
.Ar shell\-command ,
(\[aq]\[aq]) will create an empty pane with no command running in it;
.Ic display-message
.Fl I
can write to an empty pane.
The
.Fl I
flag will create an empty pane and forward any output from stdin to it.
For example:
.Bd -literal -offset indent
$ make 2>&1|tmux new\-pane \-dI &
.Ed
.Pp
See
.Ic new\-pane
for more details.
.Tg swapp
.It Xo Ic swap\-pane
.Op Fl dDUZ
@@ -4653,6 +4739,57 @@ where the number is a hexadecimal number, or a range of the form
Give the command to pipe to if the
.Ic copy\-pipe
copy mode command is used without arguments.
.It Xo Ic theme
.Op Ic detect | terminal | light | dark
.Xc
Whether tmux should use the detected client theme
.Pq Ic detect ,
use terminal ANSI colours
.Pq Ic terminal ,
or force the light or dark theme.
.It Xo Ic dark\-theme\-black ,
.Ic dark\-theme\-white ,
.Ic dark\-theme\-light\-grey ,
.Ic dark\-theme\-dark\-grey ,
.Ic dark\-theme\-green ,
.Ic dark\-theme\-yellow ,
.Ic dark\-theme\-red ,
.Ic dark\-theme\-blue ,
.Ic dark\-theme\-cyan ,
.Ic dark\-theme\-magenta ,
.Ic light\-theme\-black ,
.Ic light\-theme\-white ,
.Ic light\-theme\-light\-grey ,
.Ic light\-theme\-dark\-grey ,
.Ic light\-theme\-green ,
.Ic light\-theme\-yellow ,
.Ic light\-theme\-red ,
.Ic light\-theme\-blue ,
.Ic light\-theme\-cyan
.No and Ic light\-theme\-magenta Ar colour
.Xc
Set the theme colours.
These are the named colours
.Ic themeblack ,
.Ic themewhite ,
.Ic themelightgrey ,
.Ic themedarkgrey ,
.Ic themegreen ,
.Ic themeyellow ,
.Ic themered ,
.Ic themeblue ,
.Ic themecyan
and
.Ic thememagenta
which may be used in styles and elsewhere a
.Ar colour
is accepted.
The
.Ic dark\-theme\-*
set is used when the client reports a dark background and the
.Ic light\-theme\-*
set when it reports a light background;
an unknown background is treated as dark.
.It Ic default\-client\-command Ar command
Set the default command to run when tmux is called without a command.
The default is
@@ -5242,14 +5379,19 @@ is input before dismissing it.
Can be set to zero to disable any timeout.
.It Ic prompt\-cursor\-colour Ar colour
Set the colour of the cursor in the command prompt.
.It Ic prompt\-command\-cursor\-colour Ar colour
Set the colour of the cursor in the command prompt when
.Xr vi 1
keys are enabled and the prompt is in command mode.
.It Ic prompt\-cursor\-style Ar style
Set the style of the cursor in the command prompt.
See the
.Ic cursor\-style
options for available styles.
.It Ic prompt\-command\-cursor\-style Ar style
Set the style of the cursor in the command prompt when vi keys are enabled and
the prompt is in command mode.
Set the style of the cursor in the command prompt when
.Xr vi 1
keys are enabled and the prompt is in command mode.
See the
.Ic cursor\-style
options for available styles.
@@ -5834,7 +5976,7 @@ and
will fall back to standard ACS line drawing when UTF\-8 is not supported.
.Pp
.It Xo Ic pane\-scrollbars
.Op Ic off | modal | on
.Op Ic off | modal | on | auto\-hide
.Xc
When enabled, a character based scrollbar appears on the left or right
of each pane.
@@ -5844,19 +5986,29 @@ represents the position and size of the visible part of the pane content.
.Pp
If set to
.Ic on
the scrollbar is visible all the time.
the scrollbar is visible all the time and the pane is narrowed by the width of
the scrollbar.
If set to
.Ic modal
the scrollbar only appears when the pane is in copy mode or view mode.
When the scrollbar is visible, the pane is narrowed by the width of the
scrollbar and the text in the pane is reflowed.
the scrollbar only appears when the pane is in copy mode or view mode and
auto-hides when not in use.
If set to
.Ic modal ,
the pane is narrowed only when the scrollbar is visible.
.Ic auto\-hide
the scrollbar is available all the time but auto-hides when not in use.
With
.Ic modal
and
.Ic auto\-hide ,
the scrollbar overlays the pane and does not narrow or reflow the pane.
.Pp
See also
.Ic pane\-scrollbars\-style .
.Pp
.It Xo Ic pane\-scrollbars\-position
.Op Ic left | right
.Xc
Sets which side of the pane to display pane scrollbars on.
.Pp
.It Ic pane\-scrollbars\-style Ar style
Set the scrollbars style.
For how to specify
@@ -5873,10 +6025,13 @@ attribute sets the width of the scrollbar and the
attribute the padding between the scrollbar and the pane.
Other attributes are ignored.
.Pp
.It Xo Ic pane\-scrollbars\-position
.Op Ic left | right
.Xc
Sets which side of the pane to display pane scrollbars on.
.It Ic pane\-scrollbars\-timeout Ar time
Set the time in milliseconds before scrollbars are hidden when
.Ic pane\-scrollbars
is
.Ic modal
or
.Ic auto\-hide .
.Pp
.It Ic pane\-status\-current\-style Ar style
Set status line style for the currently active pane.
@@ -5918,6 +6073,14 @@ A value of 0 (the default) means no limit.
When a limit is set, panes are arranged to not exceed this number of columns,
with additional panes stacked in extra rows.
.Pp
.It Ic tree\-mode\-border\-style Ar style
Set the style of borders in tree mode.
For how to specify
.Ar style ,
see the
.Sx STYLES
section.
.Pp
.It Ic tree\-mode\-preview\-format Ar format
Format of the preview indicator in tree mode.
.Pp
@@ -5929,6 +6092,14 @@ see the
.Sx STYLES
section.
.Pp
.It Ic tree\-mode\-selection\-style Ar style
Set the style of the selected line in tree mode.
For how to specify
.Ar style ,
see the
.Sx STYLES
section.
.Pp
.It Ic window\-status\-activity\-style Ar style
Set status line style for windows with an activity alert.
For how to specify
@@ -6108,6 +6279,15 @@ is enabled.
When the entire screen is cleared and this option is on, scroll the contents of
the screen into history before clearing it.
.Pp
.It Ic switch\-mode\-match\-style Ar style
Set the style of characters matched by the filter in
.Ic switch\-mode .
For how to specify
.Ar style ,
see the
.Sx STYLES
section.
.Pp
.It Xo Ic synchronize\-panes
.Op Ic on | off
.Xc
@@ -6500,13 +6680,36 @@ An optional argument specifies flags:
.Ql r
means the pattern is a regular expression instead of the default
.Xr glob 7
pattern, and
pattern;
.Ql i
means to ignore case.
means to ignore case;
.Ql z
means to do a fuzzy match;
.Ql p
is like
.Ql z
but returns a list of matched positions.
A fuzzy match matches plain terms as sequences where each character must appear
in order but not necessarily consecutively; terms beginning with
.Ql '
are exact substring matches,
.Ql ^
anchors a term at the start,
.Ql $
anchors it at the end,
.Ql !
inverts a term and
.Ql |
separates alternative groups.
For example:
.Ql #{m:*foo*,#{host}}
or
.Ql #{m/ri:\[ha]A,MYVAR} .
.Ql #{m/ri:\[ha]A,MYVAR}
or
.Ql #{m/z:dev bash,dev:1 bash}
or
.Ql #{m/z:^dev | ^prod,prod:1 ssh} .
.Pp
A
.Ql C
performs a search for a
@@ -6831,6 +7034,7 @@ The following variables are available, where appropriate:
.It Li "client_activity" Ta "" Ta "Time client last had activity"
.It Li "client_cell_height" Ta "" Ta "Height of each client cell in pixels"
.It Li "client_cell_width" Ta "" Ta "Width of each client cell in pixels"
.It Li "client_colours" Ta "" Ta "Number of colours client supports"
.It Li "client_control_mode" Ta "" Ta "1 if client is in control mode"
.It Li "client_created" Ta "" Ta "Time client created"
.It Li "client_discarded" Ta "" Ta "Bytes discarded when client behind"
@@ -7146,6 +7350,28 @@ Set the width of the styled area.
.Ar N
may be a column count or a percentage (for example
.Ql 50% ) .
.It Xo Ic link=uri
(or
.Ic nolink )
.Xc
Make the styled text an OSC 8 hyperlink to
.Ar uri ,
for example
.Ql #[link=https://example.com]text#[nolink] .
This is emitted only to terminals with the
.Ic hyperlinks
feature (see
.Ic terminal-features ) ;
it works in the status line and in other formats drawn with styles.
The link continues until
.Ic nolink ,
.Ic default ,
or an empty
.Ic link=
is given.
The
.Ar uri
may not contain spaces or commas and is limited in length.
.It Xo Ic list=on ,
.Ic list=focus ,
.Ic list=left\-marker ,
@@ -7478,7 +7704,7 @@ See
for possible values for
.Ar prompt\-type .
.It Xo Ic command\-prompt
.Op Fl 1bCeFiklN
.Op Fl 1bCeFiklNP
.Op Fl I Ar inputs
.Op Fl p Ar prompts
.Op Fl t Ar target\-client
@@ -7558,6 +7784,8 @@ user exits the command prompt.
makes
.Em BSpace
cancel an empty prompt.
.Fl P
opens a prompt inside a pane instead of on the status line.
.Pp
.Fl T
tells
@@ -8016,6 +8244,7 @@ The following keys may be used in buffer mode:
.It Li "D" Ta "Delete tagged buffers"
.It Li "e" Ta "Open the buffer in an editor"
.It Li "f" Ta "Enter a format to filter items"
.It Li "c" Ta "Clear the current filter"
.It Li "O" Ta "Change sort order"
.It Li "r" Ta "Reverse sort order"
.It Li "v" Ta "Toggle preview"

21
tmux.c
View File

@@ -282,15 +282,15 @@ get_timer(void)
}
char *
clean_name(const char *name, const char* forbid)
clean_name(const char *name, int untrusted)
{
char *copy, *cp, *new_name;
if (*name == '\0' || !utf8_isvalid(name))
if (!utf8_isvalid(name))
return (NULL);
copy = xstrdup(name);
for (cp = copy; *cp != '\0'; cp++) {
if (strchr(forbid, *cp) != NULL)
if (untrusted && cp[0] == '#' && cp[1] == '(')
*cp = '_';
}
utf8_stravis(&new_name, copy, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
@@ -298,22 +298,11 @@ clean_name(const char *name, const char* forbid)
return (new_name);
}
/*
* Check a name given by a command: reject it if it is empty, not valid UTF-8,
* or contains a forbidden character. Other characters that clean_name would
* change (for example with utf8_stravis) are allowed and fixed silently.
*/
int
check_name(const char *name, const char *forbid)
check_name(const char *name)
{
const char *cp;
if (*name == '\0' || !utf8_isvalid(name))
if (!utf8_isvalid(name))
return (0);
for (cp = name; *cp != '\0'; cp++) {
if (strchr(forbid, *cp) != NULL)
return (0);
}
return (1);
}

358
tmux.h
View File

@@ -63,6 +63,10 @@ struct mouse_event;
struct options;
struct options_array_item;
struct options_entry;
struct prompt;
struct window_pane_prompt;
struct redraw_scene;
struct redraw_span;
struct screen_write_citem;
struct screen_write_cline;
struct screen_write_ctx;
@@ -96,12 +100,6 @@ struct winlink;
#define TMUX_LOCK_CMD "lock -np"
#endif
/* Forbidden characters in names. */
#define WINDOW_NAME_FORBID ":."
#define WINDOW_NAME_FORBID_EXT ":.#"
#define SESSION_NAME_FORBID ":."
#define SESSION_NAME_FORBID_EXT ":.#"
/* Minimum and maximum layout cell size, NOT including border lines. */
#define PANE_MINIMUM 1
#define PANE_MAXIMUM 10000
@@ -733,10 +731,26 @@ enum hanguljamo_state {
/* Colour flags. */
#define COLOUR_FLAG_256 0x01000000
#define COLOUR_FLAG_RGB 0x02000000
#define COLOUR_FLAG_THEME 0x04000000
/* Special colours. */
#define COLOUR_DEFAULT(c) ((c) == 8 || (c) == 9)
/* Theme colours. */
enum colour_theme {
COLOUR_THEME_BLACK,
COLOUR_THEME_WHITE,
COLOUR_THEME_LIGHT_GREY,
COLOUR_THEME_DARK_GREY,
COLOUR_THEME_GREEN,
COLOUR_THEME_YELLOW,
COLOUR_THEME_RED,
COLOUR_THEME_BLUE,
COLOUR_THEME_CYAN,
COLOUR_THEME_MAGENTA
};
#define COLOUR_THEME_COUNT 10
/* Replacement palette. */
struct colour_palette {
int fg;
@@ -796,20 +810,23 @@ struct colour_palette {
#define GRID_STRING_USED_ONLY 0x8
#define GRID_STRING_EMPTY_CELLS 0x10
/* Cell positions. */
/*
* Cell border characters. Border cells are named for the directions they
* connect to: U for up, D for down, L for left and R for right.
*/
#define CELL_INSIDE 0
#define CELL_TOPBOTTOM 1
#define CELL_LEFTRIGHT 2
#define CELL_TOPLEFT 3
#define CELL_TOPRIGHT 4
#define CELL_BOTTOMLEFT 5
#define CELL_BOTTOMRIGHT 6
#define CELL_TOPJOIN 7
#define CELL_BOTTOMJOIN 8
#define CELL_LEFTJOIN 9
#define CELL_RIGHTJOIN 10
#define CELL_JOIN 11
#define CELL_OUTSIDE 12
#define CELL_UD 1
#define CELL_LR 2
#define CELL_RD 3
#define CELL_LD 4
#define CELL_RU 5
#define CELL_LU 6
#define CELL_LRD 7
#define CELL_LRU 8
#define CELL_URD 9
#define CELL_ULD 10
#define CELL_LRUD 11
#define CELL_NONE 12
#define CELL_SCROLLBAR 13
/* Cell borders. */
@@ -970,6 +987,8 @@ struct style {
int pad;
enum style_default_type default_type;
u_int link;
};
#ifdef ENABLE_SIXEL
@@ -1113,27 +1132,6 @@ enum pane_lines {
#define WINDOW_PANE_COPY_MODE 1
#define WINDOW_PANE_VIEW_MODE 2
/* Screen redraw context. */
struct screen_redraw_ctx {
struct client *c;
u_int statuslines;
int statustop;
enum pane_lines pane_lines;
int pane_scrollbars;
int pane_scrollbars_pos;
struct grid_cell no_pane_gc;
int no_pane_gc_set;
u_int sx;
u_int sy;
int ox;
int oy;
};
/* Screen size. */
#define screen_size_x(s) ((s)->grid->sx)
#define screen_size_y(s) ((s)->grid->sy)
@@ -1285,7 +1283,7 @@ struct window_pane {
#define PANE_FOCUSED 0x4
#define PANE_VISITED 0x8
#define PANE_ZOOMED 0x10
/* 0x20 unused */
#define PANE_NEWSTATUS 0x20
#define PANE_INPUTOFF 0x40
#define PANE_CHANGED 0x80
#define PANE_EXITED 0x100
@@ -1305,6 +1303,9 @@ struct window_pane {
u_int sb_slider_y;
u_int sb_slider_h;
int sb_auto_visible;
int sb_auto_hover;
struct event sb_auto_timer;
int argc;
char **argv;
@@ -1347,13 +1348,16 @@ struct window_pane {
struct screen base;
struct screen status_screen;
size_t status_size;
TAILQ_HEAD(, window_mode_entry) modes;
char *searchstr;
int searchregex;
struct prompt *prompt;
struct window_pane_prompt *prompt_data;
u_int prompt_cx;
int border_gc_set;
struct grid_cell border_gc;
int active_border_gc_set;
@@ -1412,9 +1416,14 @@ struct window {
u_int new_xpixel;
u_int new_ypixel;
uint64_t redraw_scene_generation;
u_int last_new_pane_x;
u_int last_new_pane_y;
int sb;
int sb_pos;
struct utf8_data *fill_character;
int flags;
#define WINDOW_BELL 0x1
@@ -1474,6 +1483,7 @@ TAILQ_HEAD(winlink_stack, winlink);
#define PANE_SCROLLBARS_OFF 0
#define PANE_SCROLLBARS_MODAL 1
#define PANE_SCROLLBARS_ALWAYS 2
#define PANE_SCROLLBARS_AUTOHIDE 3
/* Pane scrollbars position option. */
#define PANE_SCROLLBARS_RIGHT 0
@@ -1513,6 +1523,12 @@ struct layout_cell {
int xoff, saved_xoff;
int yoff, saved_yoff;
u_int saved_sx;
u_int saved_sy;
int saved_xoff;
int saved_yoff;
struct window_pane *wp;
struct layout_cells cells;
@@ -2000,20 +2016,12 @@ struct status_line {
struct screen *active;
int references;
u_int prompt_cx;
struct grid_cell style;
struct style_line_entry entries[STATUS_LINES_LIMIT];
};
/* Prompt type. */
#define PROMPT_NTYPES 4
enum prompt_type {
PROMPT_TYPE_COMMAND,
PROMPT_TYPE_SEARCH,
PROMPT_TYPE_TARGET,
PROMPT_TYPE_WINDOW_TARGET,
PROMPT_TYPE_INVALID = 0xff
};
/* File in client. */
typedef void (*client_file_cb) (struct client *, const char *, int, int,
struct evbuffer *, void *);
@@ -2054,18 +2062,98 @@ RB_HEAD(client_windows, client_window);
/* Maximum time to be pasting. */
#define CLIENT_PASTE_TIME_LIMIT 5
/* Client connection. */
typedef int (*prompt_input_cb)(struct client *, void *, const char *, int);
/* Prompt type. */
#define PROMPT_NTYPES 2
enum prompt_type {
PROMPT_TYPE_COMMAND,
PROMPT_TYPE_SEARCH,
PROMPT_TYPE_INVALID = 0xff
};
/* Prompt result. */
enum prompt_result {
PROMPT_CONTINUE,
PROMPT_CLOSE
};
/* Prompt key result. */
enum prompt_key_result {
PROMPT_KEY_NOT_HANDLED,
PROMPT_KEY_HANDLED,
PROMPT_KEY_CLOSE,
PROMPT_KEY_MOVE
};
/* Prompt callbacks. */
typedef enum prompt_result (*prompt_input_cb)(void *, const char *,
enum prompt_key_result);
typedef enum prompt_result (*status_prompt_input_cb)(struct client *, void *,
const char *, enum prompt_key_result);
typedef enum prompt_result (*mode_tree_prompt_input_cb)(struct client *, void *,
const char *, enum prompt_key_result);
typedef void (*prompt_free_cb)(void *);
typedef struct visible_ranges *(*overlay_check_cb)(struct client*, void *,
/* Prompt flags. */
#define PROMPT_SINGLE 0x1
#define PROMPT_NUMERIC 0x2
#define PROMPT_INCREMENTAL 0x4
#define PROMPT_NOFORMAT 0x8
#define PROMPT_KEY 0x10
#define PROMPT_ACCEPT 0x20
#define PROMPT_QUOTENEXT 0x40
#define PROMPT_BSPACE_EXIT 0x80
#define PROMPT_NOFREEZE 0x100
#define PROMPT_COMMANDMODE 0x200
#define PROMPT_ISPANE 0x400
#define PROMPT_ISMODE 0x800
#define PROMPT_EDITARROWS 0x1000
/* Prompt create data. */
struct prompt_create_data {
struct cmd_find_state *fs;
const char *prompt;
const char *input;
enum prompt_type type;
int flags;
struct grid_cell style;
struct grid_cell command_style;
enum screen_cursor_style cstyle;
enum screen_cursor_style command_cstyle;
int ccolour;
int command_ccolour;
int cmode;
int command_cmode;
const char *message_format;
int keys;
const char *word_separators;
prompt_input_cb inputcb;
prompt_free_cb freecb;
void *data;
};
/* Prompt draw data. */
struct prompt_draw_data {
struct screen_write_ctx *ctx;
u_int *cursor_x;
u_int area_x;
u_int area_width;
u_int prompt_line;
};
/* Overlay callbacks */
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 *);
typedef void (*overlay_draw_cb)(struct client *, void *,
struct screen_redraw_ctx *);
typedef void (*overlay_draw_cb)(struct client *, void *);
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 *);
/* Client connection. */
struct client {
const char *name;
struct tmuxpeer *peer;
@@ -2108,6 +2196,8 @@ struct client {
size_t discarded;
size_t redraw;
struct redraw_scene *redraw_scene;
struct event repeat_timer;
struct event click_timer;
@@ -2198,38 +2288,15 @@ struct client {
char *message_string;
struct event message_timer;
char *prompt_string;
struct utf8_data *prompt_buffer;
struct cmd_find_state prompt_state;
char *prompt_last;
size_t prompt_index;
prompt_input_cb prompt_inputcb;
prompt_free_cb prompt_freecb;
void *prompt_data;
u_int prompt_hindex[PROMPT_NTYPES];
enum {
PROMPT_ENTRY,
PROMPT_COMMAND
} prompt_mode;
struct utf8_data *prompt_saved;
#define PROMPT_SINGLE 0x1
#define PROMPT_NUMERIC 0x2
#define PROMPT_INCREMENTAL 0x4
#define PROMPT_NOFORMAT 0x8
#define PROMPT_KEY 0x10
#define PROMPT_ACCEPT 0x20
#define PROMPT_QUOTENEXT 0x40
#define PROMPT_BSPACE_EXIT 0x80
#define PROMPT_NOFREEZE 0x100
int prompt_flags;
enum prompt_type prompt_type;
int prompt_cursor;
struct prompt *prompt;
struct session *session;
struct session *last_session;
int references;
int theme_colours[COLOUR_THEME_COUNT];
void *pan_window;
u_int pan_ox;
u_int pan_oy;
@@ -2318,6 +2385,7 @@ enum options_table_type {
#define OPTIONS_TABLE_IS_ARRAY 0x1
#define OPTIONS_TABLE_IS_HOOK 0x2
#define OPTIONS_TABLE_IS_STYLE 0x4
#define OPTIONS_TABLE_IS_COLOUR 0x8
struct options_table_entry {
const char *name;
@@ -2436,8 +2504,8 @@ int checkshell(const char *);
void setblocking(int, int);
char *shell_argv0(const char *, int);
uint64_t get_timer(void);
char *clean_name(const char *, const char *);
int check_name(const char *, const char *);
char *clean_name(const char *, int);
int check_name(const char *);
const char *sig2name(int);
const char *find_cwd(void);
const char *find_home(void);
@@ -2621,6 +2689,7 @@ char *options_to_string(struct options_entry *, int, int);
char *options_parse(const char *, int *);
struct options_entry *options_parse_get(struct options *, const char *, int *,
int);
const struct options_table_entry *options_search(const char *);
char *options_match(const char *, int *, int *);
struct options_entry *options_match_get(struct options *, const char *, int *,
int, int *);
@@ -2775,7 +2844,7 @@ void tty_cmd_setselection(struct tty *, const struct tty_ctx *);
void tty_cmd_rawstring(struct tty *, const struct tty_ctx *);
#ifdef ENABLE_SIXEL
void tty_cmd_sixelimage(struct tty *, const struct tty_ctx *);
void tty_draw_images(struct client *, struct window_pane *, struct screen *);
void tty_draw_images(struct client *, struct window_pane *);
#endif
void tty_cmd_syncstart(struct tty *, const struct tty_ctx *);
void tty_default_colours(struct grid_cell *, struct window_pane *, u_int *);
@@ -3126,6 +3195,7 @@ void server_client_print(struct client *, int, struct evbuffer *);
/* server-fn.c */
void server_redraw_client(struct client *);
void server_client_update_theme_colours(struct client *);
void server_status_client(struct client *);
void server_redraw_session(struct session *);
void server_redraw_session_group(struct session *);
@@ -3150,8 +3220,6 @@ void server_check_unattached(void);
void server_unzoom_window(struct window *);
/* status.c */
extern char **status_prompt_hlist[];
extern u_int status_prompt_hsize[];
void status_timer_start(struct client *);
void status_timer_start_all(void);
void status_update_cache(struct session *);
@@ -3167,16 +3235,38 @@ void printflike(6, 7) status_message_set(struct client *, int, int, int, int,
void status_message_clear(struct client *);
int status_message_redraw(struct client *);
void status_prompt_set(struct client *, struct cmd_find_state *,
const char *, const char *, prompt_input_cb, prompt_free_cb,
const char *, const char *, status_prompt_input_cb, prompt_free_cb,
void *, int, enum prompt_type);
void status_prompt_clear(struct client *);
int status_prompt_redraw(struct client *);
int status_prompt_key(struct client *, key_code);
void status_prompt_cursor(struct client *, u_int *, u_int *);
enum prompt_key_result status_prompt_key(struct client *, key_code,
struct mouse_event *);
void status_prompt_update(struct client *, const char *, const char *);
void status_prompt_load_history(void);
void status_prompt_save_history(void);
const char *status_prompt_type_string(u_int);
enum prompt_type status_prompt_type(const char *type);
/* prompt.c */
void prompt_set_options(struct prompt_create_data *, struct session *);
struct prompt *prompt_create(const struct prompt_create_data *);
void prompt_free(struct prompt *);
void prompt_incremental_start(struct prompt *);
void prompt_draw(struct prompt *, struct prompt_draw_data *);
enum prompt_key_result prompt_key(struct prompt *, key_code, int *);
enum prompt_key_result prompt_mouse(struct prompt *, u_int, u_int, u_int,
int *);
void prompt_update(struct prompt *, const char *, const char *);
int prompt_closed(struct prompt *);
enum prompt_type prompt_type(const char *);
const char *prompt_type_string(enum prompt_type);
/* prompt-history.c */
const char *prompt_up_history(u_int *, u_int);
const char *prompt_down_history(u_int *, u_int);
void prompt_add_history(const char *, u_int);
u_int prompt_history_size(enum prompt_type);
const char *prompt_history_get(enum prompt_type, u_int);
void prompt_history_clear(enum prompt_type);
void prompt_load_history(void);
void prompt_save_history(void);
/* resize.c */
void resize_window(struct window *, u_int, u_int, int, int);
@@ -3219,6 +3309,8 @@ int colour_dim(int, u_int);
const char *colour_tostring(int);
enum client_theme colour_totheme(int);
int colour_fromstring(const char *);
const char *colour_theme_option(u_int, enum client_theme);
int colour_theme_terminal_colour(u_int);
int colour_256toRGB(int);
int colour_256to16(int);
int colour_byname(const char *);
@@ -3234,6 +3326,9 @@ void colour_palette_from_option(struct colour_palette *, struct options *);
const char *attributes_tostring(int);
int attributes_fromstring(const char *);
/* fuzzy.c */
bitstr_t *fuzzy_match(const char *, const char *, u_int, u_int *);
/* grid.c */
extern const struct grid_cell grid_default_cell;
void grid_empty_line(struct grid *, u_int, u_int);
@@ -3339,7 +3434,8 @@ void screen_write_fast_copy(struct screen_write_ctx *, struct screen *,
u_int, u_int, u_int, u_int);
void screen_write_hline(struct screen_write_ctx *, u_int, int, int,
enum box_lines, const struct grid_cell *);
void screen_write_vline(struct screen_write_ctx *, u_int, int, int);
void screen_write_vline(struct screen_write_ctx *, u_int, int, int,
const struct grid_cell *);
void screen_write_menu(struct screen_write_ctx *, struct menu *, int,
enum box_lines, const struct grid_cell *, const struct grid_cell *,
const struct grid_cell *);
@@ -3395,8 +3491,13 @@ void screen_write_alternateoff(struct screen_write_ctx *,
struct grid_cell *, int);
/* screen-redraw.c */
void screen_redraw_screen(struct client *);
void screen_redraw_pane(struct client *, struct window_pane *, int);
void redraw_screen(struct client *);
void redraw_pane(struct client *, struct window_pane *);
void redraw_pane_scrollbar(struct client *, struct window_pane *);
void redraw_free_scene(struct redraw_scene *);
void redraw_invalidate_scene(struct window *);
void redraw_invalidate_all_scenes(void);
int redraw_get_status_border_cell_type(struct redraw_span **, u_int);
/* screen.c */
void screen_init(struct screen *, u_int, u_int, u_int);
@@ -3501,6 +3602,16 @@ int window_pane_key(struct window_pane *, struct client *,
struct mouse_event *);
void window_pane_paste(struct window_pane *, key_code, char *,
size_t);
void window_pane_set_prompt(struct window_pane *, struct client *,
struct cmd_find_state *, const char *, const char *,
status_prompt_input_cb, prompt_free_cb, void *, int,
enum prompt_type);
void window_pane_clear_prompt(struct window_pane *);
int window_pane_has_prompt(struct window_pane *);
void window_pane_update_prompt(struct window_pane *, const char *,
const char *);
enum prompt_key_result window_pane_prompt_key(struct window_pane *,
struct client *, key_code, struct mouse_event *);
int window_pane_is_visible(struct window_pane *);
int window_pane_exited(struct window_pane *);
u_int window_pane_search(struct window_pane *, const char *, int,
@@ -3515,7 +3626,7 @@ void window_pane_stack_push(struct window_panes *,
struct window_pane *);
void window_pane_stack_remove(struct window_panes *,
struct window_pane *);
void window_set_name(struct window *, const char *, const char *);
void window_set_name(struct window *, const char *, int);
void window_add_ref(struct window *, const char *);
void window_remove_ref(struct window *, const char *);
void winlink_clear_flags(struct winlink *);
@@ -3529,7 +3640,15 @@ void window_pane_update_used_data(struct window_pane *,
void window_set_fill_character(struct window *);
void window_pane_default_cursor(struct window_pane *);
int window_pane_mode(struct window_pane *);
int window_pane_show_scrollbar(struct window_pane *, int);
int window_pane_show_scrollbar(struct window_pane *);
int window_pane_scrollbar_reserve(struct window_pane *);
int window_pane_scrollbar_visible(struct window_pane *);
int window_pane_scrollbar_overlay(struct window_pane *);
int window_pane_scrollbar_overlay_visible(struct window_pane *);
void window_pane_scrollbar_show(struct window_pane *, int);
void window_pane_scrollbar_hide(struct window_pane *);
void window_pane_scrollbar_start_timer(struct window_pane *);
void window_pane_scrollbar_redraw(struct window_pane *);
int window_pane_get_bg(struct window_pane *);
int window_pane_get_fg(struct window_pane *);
int window_pane_get_fg_control_client(struct window_pane *);
@@ -3538,6 +3657,7 @@ int window_get_bg_client(struct window_pane *);
enum client_theme window_pane_get_theme(struct window_pane *);
void window_pane_send_theme_update(struct window_pane *);
enum pane_lines window_pane_get_pane_lines(struct window_pane *);
enum pane_lines window_get_pane_lines(struct window *);
int window_get_pane_status(struct window *);
int window_pane_get_pane_status(struct window_pane *);
struct style_range *window_pane_status_get_range(struct window_pane *, u_int,
@@ -3545,6 +3665,16 @@ struct style_range *window_pane_status_get_range(struct window_pane *, u_int,
int window_pane_is_floating(struct window_pane *);
int window_pane_is_hidden(struct window_pane *);
/* window-border.c */
void window_get_border_cell(struct window *, struct window_pane *,
enum pane_lines, int, struct grid_cell *);
void window_pane_get_border_cell(struct window_pane *, int,
struct grid_cell *);
void window_pane_get_border_style(struct window_pane *,
struct client *, struct grid_cell *);
int window_make_pane_status(struct window_pane *, struct client *,
u_int, struct redraw_span *);
/* window-visible.c */
int window_position_is_visible(struct visible_ranges *, u_int);
struct visible_ranges *window_visible_ranges(struct window_pane *, int, int,
@@ -3581,9 +3711,9 @@ void layout_resize_pane(struct window_pane *, enum layout_type,
int, int);
void layout_resize_pane_to(struct window_pane *, enum layout_type,
u_int);
void layout_resize_floating_pane(struct window_pane *,
int layout_resize_floating_pane(struct window_pane *,
enum layout_type, int, int, char **);
void layout_resize_floating_pane_to(struct window_pane *,
int layout_resize_floating_pane_to(struct window_pane *,
enum layout_type, u_int, char **);
void layout_assign_pane(struct layout_cell *, struct window_pane *,
int);
@@ -3591,6 +3721,8 @@ int layout_split_check_space(struct window_pane *,
struct layout_cell *, enum layout_type);
void layout_split_sizes(struct layout_cell *, int, int,
enum layout_type, u_int *, u_int *, u_int *);
struct layout_cell *layout_replace_with_node(struct window *,
struct layout_cell *, enum layout_type);
struct layout_cell *layout_split_pane(struct window_pane *, enum layout_type,
int, int);
struct layout_cell *layout_floating_pane(struct window *, struct window_pane *,
@@ -3601,10 +3733,11 @@ void layout_spread_out(struct window_pane *);
void layout_cell_floating_args_parse(struct cmdq_item *, struct args *,
struct window *, u_int *, u_int *, int *, int *, char **);
struct layout_cell *layout_get_floating_cell(struct cmdq_item *, struct args *,
struct window *, struct window_pane *, char **);
struct layout_cell *layout_get_tiled_cell(struct cmdq_item *, struct args *,
struct window *, struct window_pane *, int, char **);
int layout_cell_is_tiled(struct layout_cell *);
enum pane_lines, struct window *, struct window_pane *,
char **cause);
int layout_floating_args_parse(struct cmdq_item *, struct args *,
enum pane_lines, struct window *, u_int *, u_int *, int *,
int *, char **);
int layout_remove_tile(struct window *, struct layout_cell *);
int layout_insert_tile(struct window *, struct layout_cell *);
@@ -3634,6 +3767,7 @@ typedef const char** (*mode_tree_help_cb)(u_int *, const char**);
u_int mode_tree_count_tagged(struct mode_tree_data *);
void *mode_tree_get_current(struct mode_tree_data *);
const char *mode_tree_get_current_name(struct mode_tree_data *);
void mode_tree_select_top(struct mode_tree_data *);
void mode_tree_expand_current(struct mode_tree_data *);
void mode_tree_collapse_current(struct mode_tree_data *);
void mode_tree_expand(struct mode_tree_data *, uint64_t);
@@ -3662,6 +3796,11 @@ void mode_tree_remove(struct mode_tree_data *, struct mode_tree_item *);
void mode_tree_draw(struct mode_tree_data *);
int mode_tree_key(struct mode_tree_data *, struct client *, key_code *,
struct mouse_event *, u_int *, u_int *);
void mode_tree_set_prompt(struct mode_tree_data *, struct client *,
const char *, const char *, enum prompt_type, int,
mode_tree_prompt_input_cb, prompt_free_cb, void *);
void mode_tree_clear_prompt(struct mode_tree_data *);
int mode_tree_has_prompt(struct mode_tree_data *);
void mode_tree_run_command(struct client *, struct cmd_find_state *,
const char *, const char *);
@@ -3671,6 +3810,9 @@ extern const struct window_mode window_buffer_mode;
/* window-tree.c */
extern const struct window_mode window_tree_mode;
/* window-switch.c */
extern const struct window_mode window_switch_mode;
/* window-clock.c */
extern const struct window_mode window_clock_mode;
extern const char window_clock_table[14][5][5];
@@ -3855,8 +3997,7 @@ int menu_display(struct menu *, int, int, struct cmdq_item *,
struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *);
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_draw_cb(struct client *, void *);
void menu_free_cb(struct client *, void *);
int menu_key_cb(struct client *, void *, struct key_event *);
@@ -3877,7 +4018,10 @@ int popup_modify(struct client *, const char *, const char *,
/* style.c */
int style_parse(struct style *,const struct grid_cell *,
const char *);
int style_parse_colour(struct style *,
const struct grid_cell *, const char *);
const char *style_tostring(struct style *);
const char *style_link(struct style *);
struct style *style_add(struct grid_cell *, struct options *,
const char *, struct format_tree *);
void style_apply(struct grid_cell *, struct options *,

632
tools/x11-colours.sh Normal file
View File

@@ -0,0 +1,632 @@
#!/bin/sh
set -eu
awk '
{
if ($0 !~ /\{[[:space:]]*".*",[[:space:]]*0x[0-9A-Fa-f]+/)
next
name = $0
sub(/^[^"]*"/, "", name)
sub(/".*$/, "", name)
hex = $0
sub(/^.*0x/, "", hex)
sub(/[^0-9A-Fa-f].*$/, "", hex)
printf "%s|%s\n", name, tolower(hex)
}
' <<'COLOURS' |
{ "AliceBlue", 0xf0f8ff },
{ "AntiqueWhite", 0xfaebd7 },
{ "AntiqueWhite1", 0xffefdb },
{ "AntiqueWhite2", 0xeedfcc },
{ "AntiqueWhite3", 0xcdc0b0 },
{ "AntiqueWhite4", 0x8b8378 },
{ "BlanchedAlmond", 0xffebcd },
{ "BlueViolet", 0x8a2be2 },
{ "CadetBlue", 0x5f9ea0 },
{ "CadetBlue1", 0x98f5ff },
{ "CadetBlue2", 0x8ee5ee },
{ "CadetBlue3", 0x7ac5cd },
{ "CadetBlue4", 0x53868b },
{ "CornflowerBlue", 0x6495ed },
{ "DarkBlue", 0x00008b },
{ "DarkCyan", 0x008b8b },
{ "DarkGoldenrod", 0xb8860b },
{ "DarkGoldenrod1", 0xffb90f },
{ "DarkGoldenrod2", 0xeead0e },
{ "DarkGoldenrod3", 0xcd950c },
{ "DarkGoldenrod4", 0x8b6508 },
{ "DarkGray", 0xa9a9a9 },
{ "DarkGreen", 0x006400 },
{ "DarkGrey", 0xa9a9a9 },
{ "DarkKhaki", 0xbdb76b },
{ "DarkMagenta", 0x8b008b },
{ "DarkOliveGreen", 0x556b2f },
{ "DarkOliveGreen1", 0xcaff70 },
{ "DarkOliveGreen2", 0xbcee68 },
{ "DarkOliveGreen3", 0xa2cd5a },
{ "DarkOliveGreen4", 0x6e8b3d },
{ "DarkOrange", 0xff8c00 },
{ "DarkOrange1", 0xff7f00 },
{ "DarkOrange2", 0xee7600 },
{ "DarkOrange3", 0xcd6600 },
{ "DarkOrange4", 0x8b4500 },
{ "DarkOrchid", 0x9932cc },
{ "DarkOrchid1", 0xbf3eff },
{ "DarkOrchid2", 0xb23aee },
{ "DarkOrchid3", 0x9a32cd },
{ "DarkOrchid4", 0x68228b },
{ "DarkRed", 0x8b0000 },
{ "DarkSalmon", 0xe9967a },
{ "DarkSeaGreen", 0x8fbc8f },
{ "DarkSeaGreen1", 0xc1ffc1 },
{ "DarkSeaGreen2", 0xb4eeb4 },
{ "DarkSeaGreen3", 0x9bcd9b },
{ "DarkSeaGreen4", 0x698b69 },
{ "DarkSlateBlue", 0x483d8b },
{ "DarkSlateGray", 0x2f4f4f },
{ "DarkSlateGray1", 0x97ffff },
{ "DarkSlateGray2", 0x8deeee },
{ "DarkSlateGray3", 0x79cdcd },
{ "DarkSlateGray4", 0x528b8b },
{ "DarkSlateGrey", 0x2f4f4f },
{ "DarkTurquoise", 0x00ced1 },
{ "DarkViolet", 0x9400d3 },
{ "DeepPink", 0xff1493 },
{ "DeepPink1", 0xff1493 },
{ "DeepPink2", 0xee1289 },
{ "DeepPink3", 0xcd1076 },
{ "DeepPink4", 0x8b0a50 },
{ "DeepSkyBlue", 0x00bfff },
{ "DeepSkyBlue1", 0x00bfff },
{ "DeepSkyBlue2", 0x00b2ee },
{ "DeepSkyBlue3", 0x009acd },
{ "DeepSkyBlue4", 0x00688b },
{ "DimGray", 0x696969 },
{ "DimGrey", 0x696969 },
{ "DodgerBlue", 0x1e90ff },
{ "DodgerBlue1", 0x1e90ff },
{ "DodgerBlue2", 0x1c86ee },
{ "DodgerBlue3", 0x1874cd },
{ "DodgerBlue4", 0x104e8b },
{ "FloralWhite", 0xfffaf0 },
{ "ForestGreen", 0x228b22 },
{ "GhostWhite", 0xf8f8ff },
{ "GreenYellow", 0xadff2f },
{ "HotPink", 0xff69b4 },
{ "HotPink1", 0xff6eb4 },
{ "HotPink2", 0xee6aa7 },
{ "HotPink3", 0xcd6090 },
{ "HotPink4", 0x8b3a62 },
{ "IndianRed", 0xcd5c5c },
{ "IndianRed1", 0xff6a6a },
{ "IndianRed2", 0xee6363 },
{ "IndianRed3", 0xcd5555 },
{ "IndianRed4", 0x8b3a3a },
{ "LavenderBlush", 0xfff0f5 },
{ "LavenderBlush1", 0xfff0f5 },
{ "LavenderBlush2", 0xeee0e5 },
{ "LavenderBlush3", 0xcdc1c5 },
{ "LavenderBlush4", 0x8b8386 },
{ "LawnGreen", 0x7cfc00 },
{ "LemonChiffon", 0xfffacd },
{ "LemonChiffon1", 0xfffacd },
{ "LemonChiffon2", 0xeee9bf },
{ "LemonChiffon3", 0xcdc9a5 },
{ "LemonChiffon4", 0x8b8970 },
{ "LightBlue", 0xadd8e6 },
{ "LightBlue1", 0xbfefff },
{ "LightBlue2", 0xb2dfee },
{ "LightBlue3", 0x9ac0cd },
{ "LightBlue4", 0x68838b },
{ "LightCoral", 0xf08080 },
{ "LightCyan", 0xe0ffff },
{ "LightCyan1", 0xe0ffff },
{ "LightCyan2", 0xd1eeee },
{ "LightCyan3", 0xb4cdcd },
{ "LightCyan4", 0x7a8b8b },
{ "LightGoldenrod", 0xeedd82 },
{ "LightGoldenrod1", 0xffec8b },
{ "LightGoldenrod2", 0xeedc82 },
{ "LightGoldenrod3", 0xcdbe70 },
{ "LightGoldenrod4", 0x8b814c },
{ "LightGoldenrodYellow", 0xfafad2 },
{ "LightGray", 0xd3d3d3 },
{ "LightGreen", 0x90ee90 },
{ "LightGrey", 0xd3d3d3 },
{ "LightPink", 0xffb6c1 },
{ "LightPink1", 0xffaeb9 },
{ "LightPink2", 0xeea2ad },
{ "LightPink3", 0xcd8c95 },
{ "LightPink4", 0x8b5f65 },
{ "LightSalmon", 0xffa07a },
{ "LightSalmon1", 0xffa07a },
{ "LightSalmon2", 0xee9572 },
{ "LightSalmon3", 0xcd8162 },
{ "LightSalmon4", 0x8b5742 },
{ "LightSeaGreen", 0x20b2aa },
{ "LightSkyBlue", 0x87cefa },
{ "LightSkyBlue1", 0xb0e2ff },
{ "LightSkyBlue2", 0xa4d3ee },
{ "LightSkyBlue3", 0x8db6cd },
{ "LightSkyBlue4", 0x607b8b },
{ "LightSlateBlue", 0x8470ff },
{ "LightSlateGray", 0x778899 },
{ "LightSlateGrey", 0x778899 },
{ "LightSteelBlue", 0xb0c4de },
{ "LightSteelBlue1", 0xcae1ff },
{ "LightSteelBlue2", 0xbcd2ee },
{ "LightSteelBlue3", 0xa2b5cd },
{ "LightSteelBlue4", 0x6e7b8b },
{ "LightYellow", 0xffffe0 },
{ "LightYellow1", 0xffffe0 },
{ "LightYellow2", 0xeeeed1 },
{ "LightYellow3", 0xcdcdb4 },
{ "LightYellow4", 0x8b8b7a },
{ "LimeGreen", 0x32cd32 },
{ "MediumAquamarine", 0x66cdaa },
{ "MediumBlue", 0x0000cd },
{ "MediumOrchid", 0xba55d3 },
{ "MediumOrchid1", 0xe066ff },
{ "MediumOrchid2", 0xd15fee },
{ "MediumOrchid3", 0xb452cd },
{ "MediumOrchid4", 0x7a378b },
{ "MediumPurple", 0x9370db },
{ "MediumPurple1", 0xab82ff },
{ "MediumPurple2", 0x9f79ee },
{ "MediumPurple3", 0x8968cd },
{ "MediumPurple4", 0x5d478b },
{ "MediumSeaGreen", 0x3cb371 },
{ "MediumSlateBlue", 0x7b68ee },
{ "MediumSpringGreen", 0x00fa9a },
{ "MediumTurquoise", 0x48d1cc },
{ "MediumVioletRed", 0xc71585 },
{ "MidnightBlue", 0x191970 },
{ "MintCream", 0xf5fffa },
{ "MistyRose", 0xffe4e1 },
{ "MistyRose1", 0xffe4e1 },
{ "MistyRose2", 0xeed5d2 },
{ "MistyRose3", 0xcdb7b5 },
{ "MistyRose4", 0x8b7d7b },
{ "NavajoWhite", 0xffdead },
{ "NavajoWhite1", 0xffdead },
{ "NavajoWhite2", 0xeecfa1 },
{ "NavajoWhite3", 0xcdb38b },
{ "NavajoWhite4", 0x8b795e },
{ "NavyBlue", 0x000080 },
{ "OldLace", 0xfdf5e6 },
{ "OliveDrab", 0x6b8e23 },
{ "OliveDrab1", 0xc0ff3e },
{ "OliveDrab2", 0xb3ee3a },
{ "OliveDrab3", 0x9acd32 },
{ "OliveDrab4", 0x698b22 },
{ "OrangeRed", 0xff4500 },
{ "OrangeRed1", 0xff4500 },
{ "OrangeRed2", 0xee4000 },
{ "OrangeRed3", 0xcd3700 },
{ "OrangeRed4", 0x8b2500 },
{ "PaleGoldenrod", 0xeee8aa },
{ "PaleGreen", 0x98fb98 },
{ "PaleGreen1", 0x9aff9a },
{ "PaleGreen2", 0x90ee90 },
{ "PaleGreen3", 0x7ccd7c },
{ "PaleGreen4", 0x548b54 },
{ "PaleTurquoise", 0xafeeee },
{ "PaleTurquoise1", 0xbbffff },
{ "PaleTurquoise2", 0xaeeeee },
{ "PaleTurquoise3", 0x96cdcd },
{ "PaleTurquoise4", 0x668b8b },
{ "PaleVioletRed", 0xdb7093 },
{ "PaleVioletRed1", 0xff82ab },
{ "PaleVioletRed2", 0xee799f },
{ "PaleVioletRed3", 0xcd6889 },
{ "PaleVioletRed4", 0x8b475d },
{ "PapayaWhip", 0xffefd5 },
{ "PeachPuff", 0xffdab9 },
{ "PeachPuff1", 0xffdab9 },
{ "PeachPuff2", 0xeecbad },
{ "PeachPuff3", 0xcdaf95 },
{ "PeachPuff4", 0x8b7765 },
{ "PowderBlue", 0xb0e0e6 },
{ "RebeccaPurple", 0x663399 },
{ "RosyBrown", 0xbc8f8f },
{ "RosyBrown1", 0xffc1c1 },
{ "RosyBrown2", 0xeeb4b4 },
{ "RosyBrown3", 0xcd9b9b },
{ "RosyBrown4", 0x8b6969 },
{ "RoyalBlue", 0x4169e1 },
{ "RoyalBlue1", 0x4876ff },
{ "RoyalBlue2", 0x436eee },
{ "RoyalBlue3", 0x3a5fcd },
{ "RoyalBlue4", 0x27408b },
{ "SaddleBrown", 0x8b4513 },
{ "SandyBrown", 0xf4a460 },
{ "SeaGreen", 0x2e8b57 },
{ "SeaGreen1", 0x54ff9f },
{ "SeaGreen2", 0x4eee94 },
{ "SeaGreen3", 0x43cd80 },
{ "SeaGreen4", 0x2e8b57 },
{ "SkyBlue", 0x87ceeb },
{ "SkyBlue1", 0x87ceff },
{ "SkyBlue2", 0x7ec0ee },
{ "SkyBlue3", 0x6ca6cd },
{ "SkyBlue4", 0x4a708b },
{ "SlateBlue", 0x6a5acd },
{ "SlateBlue1", 0x836fff },
{ "SlateBlue2", 0x7a67ee },
{ "SlateBlue3", 0x6959cd },
{ "SlateBlue4", 0x473c8b },
{ "SlateGray", 0x708090 },
{ "SlateGray1", 0xc6e2ff },
{ "SlateGray2", 0xb9d3ee },
{ "SlateGray3", 0x9fb6cd },
{ "SlateGray4", 0x6c7b8b },
{ "SlateGrey", 0x708090 },
{ "SpringGreen", 0x00ff7f },
{ "SpringGreen1", 0x00ff7f },
{ "SpringGreen2", 0x00ee76 },
{ "SpringGreen3", 0x00cd66 },
{ "SpringGreen4", 0x008b45 },
{ "SteelBlue", 0x4682b4 },
{ "SteelBlue1", 0x63b8ff },
{ "SteelBlue2", 0x5cacee },
{ "SteelBlue3", 0x4f94cd },
{ "SteelBlue4", 0x36648b },
{ "VioletRed", 0xd02090 },
{ "VioletRed1", 0xff3e96 },
{ "VioletRed2", 0xee3a8c },
{ "VioletRed3", 0xcd3278 },
{ "VioletRed4", 0x8b2252 },
{ "WebGray", 0x808080 },
{ "WebGreen", 0x008000 },
{ "WebGrey", 0x808080 },
{ "WebMaroon", 0x800000 },
{ "WebPurple", 0x800080 },
{ "WhiteSmoke", 0xf5f5f5 },
{ "X11Gray", 0xbebebe },
{ "X11Green", 0x00ff00 },
{ "X11Grey", 0xbebebe },
{ "X11Maroon", 0xb03060 },
{ "X11Purple", 0xa020f0 },
{ "YellowGreen", 0x9acd32 },
{ "alice blue", 0xf0f8ff },
{ "antique white", 0xfaebd7 },
{ "aqua", 0x00ffff },
{ "aquamarine", 0x7fffd4 },
{ "aquamarine1", 0x7fffd4 },
{ "aquamarine2", 0x76eec6 },
{ "aquamarine3", 0x66cdaa },
{ "aquamarine4", 0x458b74 },
{ "azure", 0xf0ffff },
{ "azure1", 0xf0ffff },
{ "azure2", 0xe0eeee },
{ "azure3", 0xc1cdcd },
{ "azure4", 0x838b8b },
{ "beige", 0xf5f5dc },
{ "bisque", 0xffe4c4 },
{ "bisque1", 0xffe4c4 },
{ "bisque2", 0xeed5b7 },
{ "bisque3", 0xcdb79e },
{ "bisque4", 0x8b7d6b },
{ "black", 0x000000 },
{ "blanched almond", 0xffebcd },
{ "blue violet", 0x8a2be2 },
{ "blue", 0x0000ff },
{ "blue1", 0x0000ff },
{ "blue2", 0x0000ee },
{ "blue3", 0x0000cd },
{ "blue4", 0x00008b },
{ "brown", 0xa52a2a },
{ "brown1", 0xff4040 },
{ "brown2", 0xee3b3b },
{ "brown3", 0xcd3333 },
{ "brown4", 0x8b2323 },
{ "burlywood", 0xdeb887 },
{ "burlywood1", 0xffd39b },
{ "burlywood2", 0xeec591 },
{ "burlywood3", 0xcdaa7d },
{ "burlywood4", 0x8b7355 },
{ "cadet blue", 0x5f9ea0 },
{ "chartreuse", 0x7fff00 },
{ "chartreuse1", 0x7fff00 },
{ "chartreuse2", 0x76ee00 },
{ "chartreuse3", 0x66cd00 },
{ "chartreuse4", 0x458b00 },
{ "chocolate", 0xd2691e },
{ "chocolate1", 0xff7f24 },
{ "chocolate2", 0xee7621 },
{ "chocolate3", 0xcd661d },
{ "chocolate4", 0x8b4513 },
{ "coral", 0xff7f50 },
{ "coral1", 0xff7256 },
{ "coral2", 0xee6a50 },
{ "coral3", 0xcd5b45 },
{ "coral4", 0x8b3e2f },
{ "cornflower blue", 0x6495ed },
{ "cornsilk", 0xfff8dc },
{ "cornsilk1", 0xfff8dc },
{ "cornsilk2", 0xeee8cd },
{ "cornsilk3", 0xcdc8b1 },
{ "cornsilk4", 0x8b8878 },
{ "crimson", 0xdc143c },
{ "cyan", 0x00ffff },
{ "cyan1", 0x00ffff },
{ "cyan2", 0x00eeee },
{ "cyan3", 0x00cdcd },
{ "cyan4", 0x008b8b },
{ "dark blue", 0x00008b },
{ "dark cyan", 0x008b8b },
{ "dark goldenrod", 0xb8860b },
{ "dark gray", 0xa9a9a9 },
{ "dark green", 0x006400 },
{ "dark grey", 0xa9a9a9 },
{ "dark khaki", 0xbdb76b },
{ "dark magenta", 0x8b008b },
{ "dark olive green", 0x556b2f },
{ "dark orange", 0xff8c00 },
{ "dark orchid", 0x9932cc },
{ "dark red", 0x8b0000 },
{ "dark salmon", 0xe9967a },
{ "dark sea green", 0x8fbc8f },
{ "dark slate blue", 0x483d8b },
{ "dark slate gray", 0x2f4f4f },
{ "dark slate grey", 0x2f4f4f },
{ "dark turquoise", 0x00ced1 },
{ "dark violet", 0x9400d3 },
{ "deep pink", 0xff1493 },
{ "deep sky blue", 0x00bfff },
{ "dim gray", 0x696969 },
{ "dim grey", 0x696969 },
{ "dodger blue", 0x1e90ff },
{ "firebrick", 0xb22222 },
{ "firebrick1", 0xff3030 },
{ "firebrick2", 0xee2c2c },
{ "firebrick3", 0xcd2626 },
{ "firebrick4", 0x8b1a1a },
{ "floral white", 0xfffaf0 },
{ "forest green", 0x228b22 },
{ "fuchsia", 0xff00ff },
{ "gainsboro", 0xdcdcdc },
{ "ghost white", 0xf8f8ff },
{ "gold", 0xffd700 },
{ "gold1", 0xffd700 },
{ "gold2", 0xeec900 },
{ "gold3", 0xcdad00 },
{ "gold4", 0x8b7500 },
{ "goldenrod", 0xdaa520 },
{ "goldenrod1", 0xffc125 },
{ "goldenrod2", 0xeeb422 },
{ "goldenrod3", 0xcd9b1d },
{ "goldenrod4", 0x8b6914 },
{ "green yellow", 0xadff2f },
{ "green", 0x00ff00 },
{ "green1", 0x00ff00 },
{ "green2", 0x00ee00 },
{ "green3", 0x00cd00 },
{ "green4", 0x008b00 },
{ "honeydew", 0xf0fff0 },
{ "honeydew1", 0xf0fff0 },
{ "honeydew2", 0xe0eee0 },
{ "honeydew3", 0xc1cdc1 },
{ "honeydew4", 0x838b83 },
{ "hot pink", 0xff69b4 },
{ "indian red", 0xcd5c5c },
{ "indigo", 0x4b0082 },
{ "ivory", 0xfffff0 },
{ "ivory1", 0xfffff0 },
{ "ivory2", 0xeeeee0 },
{ "ivory3", 0xcdcdc1 },
{ "ivory4", 0x8b8b83 },
{ "khaki", 0xf0e68c },
{ "khaki1", 0xfff68f },
{ "khaki2", 0xeee685 },
{ "khaki3", 0xcdc673 },
{ "khaki4", 0x8b864e },
{ "lavender blush", 0xfff0f5 },
{ "lavender", 0xe6e6fa },
{ "lawn green", 0x7cfc00 },
{ "lemon chiffon", 0xfffacd },
{ "light blue", 0xadd8e6 },
{ "light coral", 0xf08080 },
{ "light cyan", 0xe0ffff },
{ "light goldenrod yellow", 0xfafad2 },
{ "light goldenrod", 0xeedd82 },
{ "light gray", 0xd3d3d3 },
{ "light green", 0x90ee90 },
{ "light grey", 0xd3d3d3 },
{ "light pink", 0xffb6c1 },
{ "light salmon", 0xffa07a },
{ "light sea green", 0x20b2aa },
{ "light sky blue", 0x87cefa },
{ "light slate blue", 0x8470ff },
{ "light slate gray", 0x778899 },
{ "light slate grey", 0x778899 },
{ "light steel blue", 0xb0c4de },
{ "light yellow", 0xffffe0 },
{ "lime green", 0x32cd32 },
{ "lime", 0x00ff00 },
{ "linen", 0xfaf0e6 },
{ "magenta", 0xff00ff },
{ "magenta1", 0xff00ff },
{ "magenta2", 0xee00ee },
{ "magenta3", 0xcd00cd },
{ "magenta4", 0x8b008b },
{ "maroon", 0xb03060 },
{ "maroon1", 0xff34b3 },
{ "maroon2", 0xee30a7 },
{ "maroon3", 0xcd2990 },
{ "maroon4", 0x8b1c62 },
{ "medium aquamarine", 0x66cdaa },
{ "medium blue", 0x0000cd },
{ "medium orchid", 0xba55d3 },
{ "medium purple", 0x9370db },
{ "medium sea green", 0x3cb371 },
{ "medium slate blue", 0x7b68ee },
{ "medium spring green", 0x00fa9a },
{ "medium turquoise", 0x48d1cc },
{ "medium violet red", 0xc71585 },
{ "midnight blue", 0x191970 },
{ "mint cream", 0xf5fffa },
{ "misty rose", 0xffe4e1 },
{ "moccasin", 0xffe4b5 },
{ "navajo white", 0xffdead },
{ "navy blue", 0x000080 },
{ "navy", 0x000080 },
{ "old lace", 0xfdf5e6 },
{ "olive drab", 0x6b8e23 },
{ "olive", 0x808000 },
{ "orange red", 0xff4500 },
{ "orange", 0xffa500 },
{ "orange1", 0xffa500 },
{ "orange2", 0xee9a00 },
{ "orange3", 0xcd8500 },
{ "orange4", 0x8b5a00 },
{ "orchid", 0xda70d6 },
{ "orchid1", 0xff83fa },
{ "orchid2", 0xee7ae9 },
{ "orchid3", 0xcd69c9 },
{ "orchid4", 0x8b4789 },
{ "pale goldenrod", 0xeee8aa },
{ "pale green", 0x98fb98 },
{ "pale turquoise", 0xafeeee },
{ "pale violet red", 0xdb7093 },
{ "papaya whip", 0xffefd5 },
{ "peach puff", 0xffdab9 },
{ "peru", 0xcd853f },
{ "pink", 0xffc0cb },
{ "pink1", 0xffb5c5 },
{ "pink2", 0xeea9b8 },
{ "pink3", 0xcd919e },
{ "pink4", 0x8b636c },
{ "plum", 0xdda0dd },
{ "plum1", 0xffbbff },
{ "plum2", 0xeeaeee },
{ "plum3", 0xcd96cd },
{ "plum4", 0x8b668b },
{ "powder blue", 0xb0e0e6 },
{ "purple", 0xa020f0 },
{ "purple1", 0x9b30ff },
{ "purple2", 0x912cee },
{ "purple3", 0x7d26cd },
{ "purple4", 0x551a8b },
{ "rebecca purple", 0x663399 },
{ "red", 0xff0000 },
{ "red1", 0xff0000 },
{ "red2", 0xee0000 },
{ "red3", 0xcd0000 },
{ "red4", 0x8b0000 },
{ "rosy brown", 0xbc8f8f },
{ "royal blue", 0x4169e1 },
{ "saddle brown", 0x8b4513 },
{ "salmon", 0xfa8072 },
{ "salmon1", 0xff8c69 },
{ "salmon2", 0xee8262 },
{ "salmon3", 0xcd7054 },
{ "salmon4", 0x8b4c39 },
{ "sandy brown", 0xf4a460 },
{ "sea green", 0x2e8b57 },
{ "seashell", 0xfff5ee },
{ "seashell1", 0xfff5ee },
{ "seashell2", 0xeee5de },
{ "seashell3", 0xcdc5bf },
{ "seashell4", 0x8b8682 },
{ "sienna", 0xa0522d },
{ "sienna1", 0xff8247 },
{ "sienna2", 0xee7942 },
{ "sienna3", 0xcd6839 },
{ "sienna4", 0x8b4726 },
{ "silver", 0xc0c0c0 },
{ "sky blue", 0x87ceeb },
{ "slate blue", 0x6a5acd },
{ "slate gray", 0x708090 },
{ "slate grey", 0x708090 },
{ "snow", 0xfffafa },
{ "snow1", 0xfffafa },
{ "snow2", 0xeee9e9 },
{ "snow3", 0xcdc9c9 },
{ "snow4", 0x8b8989 },
{ "spring green", 0x00ff7f },
{ "steel blue", 0x4682b4 },
{ "tan", 0xd2b48c },
{ "tan1", 0xffa54f },
{ "tan2", 0xee9a49 },
{ "tan3", 0xcd853f },
{ "tan4", 0x8b5a2b },
{ "teal", 0x008080 },
{ "thistle", 0xd8bfd8 },
{ "thistle1", 0xffe1ff },
{ "thistle2", 0xeed2ee },
{ "thistle3", 0xcdb5cd },
{ "thistle4", 0x8b7b8b },
{ "tomato", 0xff6347 },
{ "tomato1", 0xff6347 },
{ "tomato2", 0xee5c42 },
{ "tomato3", 0xcd4f39 },
{ "tomato4", 0x8b3626 },
{ "turquoise", 0x40e0d0 },
{ "turquoise1", 0x00f5ff },
{ "turquoise2", 0x00e5ee },
{ "turquoise3", 0x00c5cd },
{ "turquoise4", 0x00868b },
{ "violet red", 0xd02090 },
{ "violet", 0xee82ee },
{ "web gray", 0x808080 },
{ "web green", 0x008000 },
{ "web grey", 0x808080 },
{ "web maroon", 0x800000 },
{ "web purple", 0x800080 },
{ "wheat", 0xf5deb3 },
{ "wheat1", 0xffe7ba },
{ "wheat2", 0xeed8ae },
{ "wheat3", 0xcdba96 },
{ "wheat4", 0x8b7e66 },
{ "white smoke", 0xf5f5f5 },
{ "white", 0xffffff },
{ "x11 gray", 0xbebebe },
{ "x11 green", 0x00ff00 },
{ "x11 grey", 0xbebebe },
{ "x11 maroon", 0xb03060 },
{ "x11 purple", 0xa020f0 },
{ "yellow green", 0x9acd32 },
{ "yellow", 0xffff00 },
{ "yellow1", 0xffff00 },
{ "yellow2", 0xeeee00 },
{ "yellow3", 0xcdcd00 },
{ "yellow4", 0x8b8b00 }
COLOURS
LC_ALL=C sort -f |
awk -F '|' '
function h2d(s, i, n, c, p) {
n = 0
for (i = 1; i <= length(s); i++) {
c = substr(s, i, 1)
p = index("0123456789abcdef", tolower(c))
if (p == 0)
return -1
n = n * 16 + p - 1
}
return n
}
{
name[NR] = $1
hex[NR] = $2
if (length($1) > width)
width = length($1)
}
END {
esc = sprintf("%c", 27)
for (i = 1; i <= NR; i++) {
r = h2d(substr(hex[i], 1, 2))
g = h2d(substr(hex[i], 3, 2))
b = h2d(substr(hex[i], 5, 2))
printf "%-*s %s[38;2;%d;%d;%dmXXXXXXXX%s[0m %s[48;2;%d;%d;%dm %s[0m\n", \
width, name[i], esc, r, g, b, esc, esc, r, g, b, esc
}
}
'

View File

@@ -580,6 +580,7 @@ tty_default_features(int *feat, const char *name, u_int version)
"cstyle,"
"extkeys,"
"focus,"
"hyperlinks,"
"usstyle"
},
{ .name = "XTerm",

41
tty.c
View File

@@ -51,6 +51,7 @@ static void tty_check_bg(struct tty *, struct colour_palette *,
struct grid_cell *);
static void tty_check_us(struct tty *, struct colour_palette *,
struct grid_cell *);
static int tty_map_theme_colour(struct tty *, int);
static void tty_colours_fg(struct tty *, const struct grid_cell *);
static void tty_colours_bg(struct tty *, const struct grid_cell *);
static void tty_colours_us(struct tty *, const struct grid_cell *);
@@ -752,8 +753,10 @@ tty_force_cursor_colour(struct tty *tty, int c)
u_char r, g, b;
char s[13];
if (c != -1)
if (c != -1) {
c = tty_map_theme_colour(tty, c);
c = colour_force_rgb(c);
}
if (c == tty->ccolour)
return;
if (c == -1)
@@ -1545,20 +1548,20 @@ tty_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
}
void
tty_draw_images(struct client *c, struct window_pane *wp, struct screen *s)
tty_draw_images(struct client *c, struct window_pane *wp)
{
struct image *im;
struct tty_ctx ttyctx;
TAILQ_FOREACH(im, &s->images, entry) {
TAILQ_FOREACH(im, &wp->screen->images, entry) {
memset(&ttyctx, 0, sizeof ttyctx);
/* Set the client independent properties. */
ttyctx.ocx = im->px;
ttyctx.ocy = im->py;
ttyctx.orlower = s->rlower;
ttyctx.orupper = s->rupper;
ttyctx.orlower = wp->screen->rlower;
ttyctx.orupper = wp->screen->rupper;
ttyctx.xoff = ttyctx.rxoff = wp->xoff;
ttyctx.sx = wp->sx;
@@ -2780,6 +2783,9 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc,
gc2.bg = changed;
}
}
gc2.fg = tty_map_theme_colour(tty, gc2.fg);
gc2.bg = tty_map_theme_colour(tty, gc2.bg);
gc2.us = tty_map_theme_colour(tty, gc2.us);
if (style_ctx->dim != 0) {
gc2.fg = tty_dim_default_colour(tty, gc2.fg, 1);
gc2.bg = tty_dim_default_colour(tty, gc2.bg, 0);
@@ -2929,6 +2935,28 @@ tty_colours(struct tty *tty, const struct grid_cell *gc)
tty_colours_us(tty, gc);
}
static int
tty_map_theme_colour(struct tty *tty, int colour)
{
struct client *c;
u_int n;
int m;
if (~colour & COLOUR_FLAG_THEME)
return (colour);
n = colour & 0xff;
if (n >= COLOUR_THEME_COUNT)
return (8);
if (tty == NULL || (c = tty->client) == NULL)
return (8);
m = c->theme_colours[n];
if (m == -1 || (m & COLOUR_FLAG_THEME))
return (8);
return (m);
}
static void
tty_check_fg(struct tty *tty, struct colour_palette *palette,
struct grid_cell *gc)
@@ -2951,6 +2979,7 @@ tty_check_fg(struct tty *tty, struct colour_palette *palette,
if ((c = colour_palette_get(palette, c)) != -1)
gc->fg = c;
}
gc->fg = tty_map_theme_colour(tty, gc->fg);
/* Is this a 24-bit colour? */
if (gc->fg & COLOUR_FLAG_RGB) {
@@ -3011,6 +3040,7 @@ tty_check_bg(struct tty *tty, struct colour_palette *palette,
if ((c = colour_palette_get(palette, gc->bg)) != -1)
gc->bg = c;
}
gc->bg = tty_map_theme_colour(tty, gc->bg);
/* Is this a 24-bit colour? */
if (gc->bg & COLOUR_FLAG_RGB) {
@@ -3061,6 +3091,7 @@ tty_check_us(__unused struct tty *tty, struct colour_palette *palette,
if ((c = colour_palette_get(palette, gc->us)) != -1)
gc->us = c;
}
gc->us = tty_map_theme_colour(tty, gc->us);
/* Convert underscore colour if only RGB can be supported. */
if (!tty_term_has(tty->term, TTYC_SETULC1)) {

171
window-border.c Normal file
View File

@@ -0,0 +1,171 @@
/* $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 <stdlib.h>
#include <string.h>
#include "tmux.h"
/* Get border cell. */
void
window_get_border_cell(struct window *w, struct window_pane *wp,
enum pane_lines pane_lines, int cell_type, struct grid_cell *gc)
{
u_int idx;
if (cell_type == CELL_NONE && w->fill_character != NULL) {
utf8_copy(&gc->data, &w->fill_character[0]);
return;
}
switch (pane_lines) {
case PANE_LINES_NUMBER:
if (cell_type == CELL_NONE) {
gc->attr |= GRID_ATTR_CHARSET;
utf8_set(&gc->data, CELL_BORDERS[CELL_NONE]);
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, tty_acs_double_borders(cell_type));
break;
case PANE_LINES_HEAVY:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
break;
case PANE_LINES_SIMPLE:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
break;
case PANE_LINES_NONE:
case PANE_LINES_SPACES:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_set(&gc->data, ' ');
break;
default:
gc->attr |= GRID_ATTR_CHARSET;
utf8_set(&gc->data, CELL_BORDERS[cell_type]);
break;
}
}
/* Get pane border cell. */
void
window_pane_get_border_cell(struct window_pane *wp, int cell_type,
struct grid_cell *gc)
{
enum pane_lines pane_lines = window_pane_get_pane_lines(wp);
window_get_border_cell(wp->window, wp, pane_lines, cell_type, gc);
}
/* Get pane border style. */
void
window_pane_get_border_style(struct window_pane *wp, struct client *c,
struct grid_cell *gc)
{
struct session *s = c->session;
struct format_tree *ft;
const char *option;
struct grid_cell *saved;
int *flag;
if (wp == server_client_get_pane(c)) {
flag = &wp->active_border_gc_set;
saved = &wp->active_border_gc;
option = "pane-active-border-style";
} else {
flag = &wp->border_gc_set;
saved = &wp->border_gc;
option = "pane-border-style";
}
if (!*flag) {
ft = format_create_defaults(NULL, c, s, s->curw, wp);
style_apply(saved, wp->options, option, ft);
format_free(ft);
*flag = 1;
}
memcpy(gc, saved, sizeof *gc);
}
/* Build pane status line. */
int
window_make_pane_status(struct window_pane *wp, struct client *c, u_int width,
struct redraw_span *span)
{
struct grid_cell gc;
const char *fmt;
struct format_tree *ft;
struct style_line_entry *sle = &wp->border_status_line;
struct screen_write_ctx ctx;
struct screen old;
char *expanded;
u_int i;
enum pane_lines pane_lines;
int pane_status, cell_type;
pane_status = window_pane_get_pane_status(wp);
if (pane_status == PANE_STATUS_OFF || width == 0)
return (0);
ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS);
format_defaults(ft, c, c->session, c->session->curw, wp);
fmt = options_get_string(wp->options, "pane-border-format");
expanded = format_expand_time(ft, fmt);
memcpy(&old, &wp->status_screen, sizeof old);
screen_init(&wp->status_screen, width, 1, 0);
wp->status_screen.mode = 0;
screen_write_start(&ctx, &wp->status_screen);
window_pane_get_border_style(wp, c, &gc);
pane_lines = window_pane_get_pane_lines(wp);
for (i = 0; i < width; i++) {
cell_type = redraw_get_status_border_cell_type(&span, i);
window_get_border_cell(wp->window, wp, pane_lines, cell_type, &gc);
screen_write_cell(&ctx, &gc);
}
gc.attr &= ~GRID_ATTR_CHARSET;
screen_write_cursormove(&ctx, 0, 0, 0);
style_ranges_free(&sle->ranges);
format_draw(&ctx, &gc, width, expanded, &sle->ranges, 0);
screen_write_stop(&ctx);
format_free(ft);
free(sle->expanded);
sle->expanded = expanded;
if (grid_compare(wp->status_screen.grid, old.grid) == 0) {
screen_free(&old);
return (0);
}
screen_free(&old);
return (1);
}

View File

@@ -336,13 +336,20 @@ window_buffer_sort(struct sort_criteria *sort_crit)
}
static const char* window_buffer_help_lines[] = {
"#[bold] Enter #[default]#[acs]x#[default] Paste selected %1",
"#[bold] p #[default]#[acs]x#[default] Paste selected %1",
"#[bold] P #[default]#[acs]x#[default] Paste tagged %1s",
"#[bold] d #[default]#[acs]x#[default] Delete selected %1",
"#[bold] D #[default]#[acs]x#[default] Delete tagged %1s",
"#[bold] e #[default]#[acs]x#[default] Open %1 in editor",
"#[bold] f #[default]#[acs]x#[default] Enter a filter",
"#[fg=themelightgrey]"
" Enter #[#{E:tree-mode-border-style},acs]x#[default] Paste selected %1",
"#[fg=themelightgrey]"
" p #[#{E:tree-mode-border-style},acs]x#[default] Paste selected %1",
"#[fg=themelightgrey]"
" P #[#{E:tree-mode-border-style},acs]x#[default] Paste tagged %1s",
"#[fg=themelightgrey]"
" d #[#{E:tree-mode-border-style},acs]x#[default] Delete selected %1",
"#[fg=themelightgrey]"
" D #[#{E:tree-mode-border-style},acs]x#[default] Delete tagged %1s",
"#[fg=themelightgrey]"
" e #[#{E:tree-mode-border-style},acs]x#[default] Open %1 in editor",
"#[fg=themelightgrey]"
" f #[#{E:tree-mode-border-style},acs]x#[default] Enter a filter",
NULL
};

View File

@@ -38,7 +38,7 @@ static void window_client_key(struct window_mode_entry *,
#define WINDOW_CLIENT_DEFAULT_COMMAND "detach-client -t '%%'"
#define WINDOW_CLIENT_DEFAULT_FORMAT \
"#{t/p:client_activity}: session #{session_name}"
"#[fg=themelightgrey]#{t/p:client_activity}: session #[default]#{session_name}"
#define WINDOW_CLIENT_DEFAULT_KEY_FORMAT \
"#{?#{e|<:#{line},10}," \
@@ -49,87 +49,91 @@ static void window_client_key(struct window_mode_entry *,
#define WINDOW_CLIENT_FEATURE(f) \
"#{?#{I/f:" #f "}," \
"#[fg=green],#[dim]}#{p/15:#{l:" #f "}}" \
"#[fg=themegreen],#[fg=themelightgrey]}#{p/15:#{l:" #f "}}" \
"#[default]"
static const char *window_client_info_lines[] = {
"Client Name #[acs]x#[default] "
"#{client_name} "
"#[dim](PID #{client_pid})#[default]",
"Session #[acs]x#[default] "
"#[fg=themelightgrey]Client Name #[#{E:tree-mode-border-style},acs]x#[default] "
"#{client_name} #[fg=themelightgrey]"
"#[fg=themelightgrey](PID #{client_pid})#[default]",
"#[fg=themelightgrey]Session #[#{E:tree-mode-border-style},acs]x#[default] "
"#{session_name}",
"Attach Time #[acs]x#[default] "
"#[fg=themelightgrey]Attach Time #[#{E:tree-mode-border-style},acs]x#[default] "
"#{t:client_created} "
"#[dim](#{t/r:client_created})#[default]",
"Activity Time #[acs]x#[default] "
"#[fg=themelightgrey](#{t/r:client_created})#[default]",
"#[fg=themelightgrey]Activity Time #[#{E:tree-mode-border-style},acs]x#[default] "
"#{t:client_activity} "
"#[dim](#{t/r:client_activity})#[default]",
"Terminal Type #[acs]x#[default] "
"#[fg=themelightgrey](#{t/r:client_activity})#[default]",
"#[fg=themelightgrey]Terminal Type #[#{E:tree-mode-border-style},acs]x#[default] "
"#{?client_termtype,#{client_termtype},Unknown}",
"TERM #[acs]x#[default] "
"#[fg=themelightgrey]TERM #[#{E:tree-mode-border-style},acs]x#[default] "
"#{client_termname}",
"Size #[acs]x#[default] "
"#[fg=themelightgrey]Size #[#{E:tree-mode-border-style},acs]x#[default] "
"#{client_width}x#{client_height} "
"#[dim](cell #{client_cell_width}x#{client_cell_height})#[default]",
"Bytes Written #[acs]x#[default] "
"#[fg=themelightgrey](cell #{client_cell_width}x"
"#{client_cell_height})#[default]",
"#[fg=themelightgrey]Bytes Written #[#{E:tree-mode-border-style},acs]x#[default] "
"#{client_written} "
"#[dim](#{client_discarded} discarded)#[default]",
"#[fg=themelightgrey](#{client_discarded} discarded)#[default]",
"Features #[acs]x#[default] "
"#[fg=themelightgrey]Features #[#{E:tree-mode-border-style},acs]x#[default] "
WINDOW_CLIENT_FEATURE(256) " "
WINDOW_CLIENT_FEATURE(RGB) " "
WINDOW_CLIENT_FEATURE(bpaste) " "
WINDOW_CLIENT_FEATURE(ccolour),
" #[acs]x#[default] "
" #[#{E:tree-mode-border-style},acs]x#[default] "
WINDOW_CLIENT_FEATURE(clipboard) " "
WINDOW_CLIENT_FEATURE(cstyle) " "
WINDOW_CLIENT_FEATURE(extkeys) " "
WINDOW_CLIENT_FEATURE(focus),
" #[acs]x#[default] "
" #[#{E:tree-mode-border-style},acs]x#[default] "
WINDOW_CLIENT_FEATURE(hyperlinks) " "
WINDOW_CLIENT_FEATURE(ignorefkeys) " "
WINDOW_CLIENT_FEATURE(margins) " "
WINDOW_CLIENT_FEATURE(mouse),
" #[acs]x#[default] "
" #[#{E:tree-mode-border-style},acs]x#[default] "
WINDOW_CLIENT_FEATURE(osc7) " "
WINDOW_CLIENT_FEATURE(overline) " "
WINDOW_CLIENT_FEATURE(progressbar) " "
WINDOW_CLIENT_FEATURE(rectfill),
" #[acs]x#[default] "
" #[#{E:tree-mode-border-style},acs]x#[default] "
WINDOW_CLIENT_FEATURE(sixel) " "
WINDOW_CLIENT_FEATURE(strikethrough) " "
WINDOW_CLIENT_FEATURE(sync) " "
WINDOW_CLIENT_FEATURE(title),
" #[acs]x#[default] "
" #[#{E:tree-mode-border-style},acs]x#[default] "
WINDOW_CLIENT_FEATURE(usstyle),
"#[acs]qqqqqqqqqqqqqqn#{R:q,#{window_width}}#[default]",
"#[#{E:tree-mode-border-style},acs]qqqqqqqqqqqqqqn#{R:q,#{window_width}}#[default]",
"prefix #[acs]x#[default] "
"#[fg=themelightgrey]prefix #[#{E:tree-mode-border-style},acs]x#[default] "
"#{prefix}",
"mouse #[acs]x#[default] "
"#{?mouse,#{?#{I/c:kmous},,#[fg=red]}on,#[dim]off} "
"#[fg=themelightgrey]mouse #[#{E:tree-mode-border-style},acs]x#[default] "
"#{?mouse,#{?#{I/c:kmous},,#[fg=themered]}on,#[fg=themelightgrey]off} "
"#{?#{I/c:kmous},,#[align=right]unavailable: [kmous] missing}",
"set-clipboard #[acs]x#[default] "
"#{?#{!=:#{set-clipboard},off},#{?#{I/f:clipboard},,#[fg=red]}#{set-clipboard},#[dim]off} "
"#[fg=themelightgrey]set-clipboard #[#{E:tree-mode-border-style},acs]x#[default] "
"#{?#{!=:#{set-clipboard},off},#{?#{I/f:clipboard},,"
"#[fg=themered]}#{set-clipboard},#[fg=themelightgrey]off} "
"#{?#{I/f:clipboard},,#[align=right]unavailable: [Ms] missing}",
"get-clipboard #[acs]x#[default] "
"#{?#{!=:#{get-clipboard},off},#{?#{I/f:clipboard},,#[fg=red]}#{get-clipboard},#[dim]off} "
"#[fg=themelightgrey]get-clipboard #[#{E:tree-mode-border-style},acs]x#[default] "
"#{?#{!=:#{get-clipboard},off},#{?#{I/f:clipboard},,"
"#[fg=themered]}#{get-clipboard},#[fg=themelightgrey]off} "
"#{?#{I/f:clipboard},,#[align=right]unavailable: [Ms] missing}",
"focus-events #[acs]x#[default] "
"#{?focus-events,#{?#{I/f:focus},,#[fg=red]}on,#[dim]off} "
"#[fg=themelightgrey]focus-events #[#{E:tree-mode-border-style},acs]x#[default] "
"#{?focus-events,#{?#{I/f:focus},,#[fg=themered]}on,#[fg=themelightgrey]off} "
"#{?#{I/f:focus},,#[align=right]unavailable: [Enfcs] or [Dcfcs] missing}",
"extended-keys #[acs]x#[default] "
"#{?#{!=:#{extended-keys},off},#{?#{I/f:extkeys},,#[fg=red]}#{extended-keys},#[dim]off} "
"#[fg=themelightgrey]extended-keys #[#{E:tree-mode-border-style},acs]x#[default] "
"#{?#{!=:#{extended-keys},off},#{?#{I/f:extkeys},,"
"#[fg=themered]}#{extended-keys},#[fg=themelightgrey]off} "
"#{?#{I/f:extkeys},,#[align=right]unavailable: [Eneks] or [Dseks] missing}",
"set-titles #[acs]x#[default] "
"#{?set-titles,on,#[dim]off}",
"#[fg=themelightgrey]set-titles #[#{E:tree-mode-border-style},acs]x#[default] "
"#{?set-titles,on,#[fg=themelightgrey]off}",
"escape-time #[acs]x#[default] "
"#[fg=themelightgrey]escape-time #[#{E:tree-mode-border-style},acs]x#[default] "
"#{escape-time} ms",
};
@@ -258,6 +262,8 @@ window_client_draw_info(__unused void *modedata, void *itemdata,
struct window_client_itemdata *item = itemdata;
struct client *c = item->c;
struct screen *s = ctx->s;
struct window *w = c->session->curw->window;
struct grid_cell gc;
u_int cx = s->cx, cy = s->cy, i;
struct format_tree *ft;
char *expanded;
@@ -273,6 +279,12 @@ window_client_draw_info(__unused void *modedata, void *itemdata,
format_draw(ctx, &grid_default_cell, sx, expanded, NULL, 0);
free(expanded);
}
if (sx > 14 && i < sy) {
memcpy(&gc, &grid_default_cell, sizeof gc);
style_apply(&gc, w->options, "tree-mode-border-style", NULL);
screen_write_cursormove(ctx, cx + 14, cy + i, 0);
screen_write_vline(ctx, sy - i, 0, 0, &gc);
}
format_free(ft);
}
@@ -284,20 +296,24 @@ window_client_draw(void *modedata, void *itemdata,
struct window_client_modedata *data = modedata;
struct window_client_itemdata *item = itemdata;
struct client *c = item->c;
struct session *session = c->session;
struct screen *s = ctx->s;
struct window *w;
struct window_pane *wp;
struct grid_cell gc;
u_int cx = s->cx, cy = s->cy, lines, at;
if (c->session == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
if (session == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
return;
if (data->preview_is_info) {
window_client_draw_info(modedata, itemdata, ctx, sx, sy);
return;
}
wp = c->session->curw->window->active;
w = session->curw->window;
wp = w->active;
if (data->hide_preview_this_pane && wp == data->wp) {
if (!TAILQ_EMPTY(&c->session->curw->window->last_panes))
wp = TAILQ_FIRST(&c->session->curw->window->last_panes);
if (!TAILQ_EMPTY(&w->last_panes))
wp = TAILQ_FIRST(&w->last_panes);
else
wp = NULL;
}
@@ -318,7 +334,9 @@ window_client_draw(void *modedata, void *itemdata,
screen_write_cursormove(ctx, cx, cy + 2, 0);
else
screen_write_cursormove(ctx, cx, cy + sy - 1 - lines, 0);
screen_write_hline(ctx, sx, 0, 0, BOX_LINES_DEFAULT, NULL);
memcpy(&gc, &grid_default_cell, sizeof gc);
style_apply(&gc, w->options, "tree-mode-border-style", NULL);
screen_write_hline(ctx, sx, 0, 0, BOX_LINES_DEFAULT, &gc);
if (at != 0)
screen_write_cursormove(ctx, cx, cy, 0);
@@ -369,15 +387,24 @@ window_client_sort(struct sort_criteria *sort_crit)
}
static const char* window_client_help_lines[] = {
"#[bold] i #[default]#[acs]x#[default] Toggle info view",
"#[bold] Enter #[default]#[acs]x#[default] Choose selected %1",
"#[bold] d #[default]#[acs]x#[default] Detach selected %1",
"#[bold] D #[default]#[acs]x#[default] Detach tagged %1s",
"#[bold] x #[default]#[acs]x#[default] Detach selected %1",
"#[bold] X #[default]#[acs]x#[default] Detach tagged %1s",
"#[bold] z #[default]#[acs]x#[default] Suspend selected %1",
"#[bold] Z #[default]#[acs]x#[default] Suspend tagged %1s",
"#[bold] f #[default]#[acs]x#[default] Enter a filter",
"#[fg=themelightgrey]"
" i #[#{E:tree-mode-border-style},acs]x#[default] Toggle info view",
"#[fg=themelightgrey]"
" Enter #[#{E:tree-mode-border-style},acs]x#[default] Choose selected %1",
"#[fg=themelightgrey]"
" d #[#{E:tree-mode-border-style},acs]x#[default] Detach selected %1",
"#[fg=themelightgrey]"
" D #[#{E:tree-mode-border-style},acs]x#[default] Detach tagged %1s",
"#[fg=themelightgrey]"
" x #[#{E:tree-mode-border-style},acs]x#[default] Detach selected %1",
"#[fg=themelightgrey]"
" X #[#{E:tree-mode-border-style},acs]x#[default] Detach tagged %1s",
"#[fg=themelightgrey]"
" z #[#{E:tree-mode-border-style},acs]x#[default] Suspend selected %1",
"#[fg=themelightgrey]"
" Z #[#{E:tree-mode-border-style},acs]x#[default] Suspend tagged %1s",
"#[fg=themelightgrey]"
" f #[#{E:tree-mode-border-style},acs]x#[default] Enter a filter",
NULL
};

View File

@@ -222,18 +222,23 @@ static void
window_clock_draw_screen(struct window_mode_entry *wme)
{
struct window_pane *wp = wme->wp;
struct window *w = wp->window;
struct window_clock_mode_data *data = wme->data;
struct screen_write_ctx ctx;
int colour, style;
struct screen *s = &data->screen;
struct grid_cell gc;
struct format_tree *ft;
char tim[64], *ptr;
time_t t;
struct tm *tm;
u_int i, j, x, y, idx;
colour = options_get_number(wp->window->options, "clock-mode-colour");
style = options_get_number(wp->window->options, "clock-mode-style");
ft = format_create_defaults(NULL, NULL, NULL, NULL, wp);
style_apply(&gc, w->options, "clock-mode-colour", ft);
format_free(ft);
colour = gc.fg;
style = options_get_number(w->options, "clock-mode-style");
screen_write_start(&ctx, s);

View File

@@ -61,8 +61,10 @@ static int window_copy_line_number_mode(struct window_mode_entry *);
static int window_copy_line_number_is_absolute(struct window_mode_entry *);
static int window_copy_line_numbers_active(struct window_mode_entry *);
static u_int window_copy_line_number_width(struct window_mode_entry *);
static u_int window_copy_cursor_offset(struct window_mode_entry *, u_int, u_int);
static u_int window_copy_cursor_unoffset(struct window_mode_entry *, u_int, u_int);
static u_int window_copy_cursor_offset(struct window_mode_entry *, u_int,
u_int);
static u_int window_copy_cursor_unoffset(struct window_mode_entry *, u_int,
u_int);
static void window_copy_write_line(struct window_mode_entry *,
struct screen_write_ctx *, u_int);
static void window_copy_write_lines(struct window_mode_entry *,
@@ -800,7 +802,7 @@ window_copy_scroll1(struct window_mode_entry *wme, struct window_pane *wp,
return;
/*
* See screen_redraw_draw_pane_scrollbar - this is the inverse of the
* See redraw_draw_pane_scrollbar - this is the inverse of the
* formula used there.
*/
new_offset = new_slider_y * ((float)(size + sb_height) / sb_height);
@@ -860,6 +862,7 @@ window_copy_scroll1(struct window_mode_entry *wme, struct window_pane *wp,
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
window_copy_update_selection(wme, 1, 0);
window_pane_scrollbar_show(wp, 1);
window_copy_redraw_screen(wme);
}
@@ -913,6 +916,7 @@ window_copy_pageup1(struct window_mode_entry *wme, int half_page)
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
window_copy_update_selection(wme, 1, 0);
window_pane_scrollbar_show(wme->wp, 1);
window_copy_redraw_screen(wme);
}
@@ -973,6 +977,7 @@ window_copy_pagedown1(struct window_mode_entry *wme, int half_page,
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
window_copy_update_selection(wme, 1, 0);
window_pane_scrollbar_show(wme->wp, 1);
window_copy_redraw_screen(wme);
return (0);
}
@@ -1789,7 +1794,7 @@ window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs)
struct window_mode_entry *wme = cs->wme;
struct window_copy_mode_data *data = wme->data;
struct screen *s = data->backing;
u_int oy;
u_int oy, old_oy = data->oy;
oy = screen_hsize(s) + data->cy - data->oy;
if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
@@ -1802,6 +1807,8 @@ window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs)
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
window_copy_update_selection(wme, 1, 0);
if (data->oy != old_oy)
window_pane_scrollbar_show(wme->wp, 1);
return (WINDOW_COPY_CMD_REDRAW);
}
@@ -1810,7 +1817,7 @@ window_copy_cmd_history_top(struct window_copy_cmd_state *cs)
{
struct window_mode_entry *wme = cs->wme;
struct window_copy_mode_data *data = wme->data;
u_int oy;
u_int oy, old_oy = data->oy;
oy = screen_hsize(data->backing) + data->cy - data->oy;
if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
@@ -1823,6 +1830,8 @@ window_copy_cmd_history_top(struct window_copy_cmd_state *cs)
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
window_copy_update_selection(wme, 1, 0);
if (data->oy != old_oy)
window_pane_scrollbar_show(wme->wp, 1);
return (WINDOW_COPY_CMD_REDRAW);
}
@@ -3772,7 +3781,7 @@ window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py,
{
struct window_copy_mode_data *data = wme->data;
struct grid *gd = data->backing->grid;
u_int offset, gap;
u_int offset, gap, old_oy = data->oy;
data->cx = px;
@@ -3796,6 +3805,8 @@ window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py,
if (!no_redraw && data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
window_copy_update_selection(wme, 1, 0);
if (data->oy != old_oy)
window_pane_scrollbar_show(wme->wp, 1);
if (!no_redraw)
window_copy_redraw_screen(wme);
}
@@ -5154,6 +5165,9 @@ window_copy_write_line(struct window_mode_entry *wme,
else
content_sx = sx;
screen_write_cursormove(ctx, 0, py, 0);
screen_write_clearline(ctx, 8);
ft = format_create_defaults(NULL, NULL, NULL, NULL, wp);
style_apply(&gc, oo, "copy-mode-position-style", ft);
@@ -5275,7 +5289,10 @@ window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
return;
}
screen_write_start_pane(&ctx, wp, NULL);
if (window_pane_scrollbar_overlay_visible(wp))
screen_write_start(&ctx, &data->screen);
else
screen_write_start_pane(&ctx, wp, NULL);
for (i = py; i < py + ny; i++)
window_copy_write_line(wme, &ctx, i);
screen_write_cursormove(&ctx,
@@ -5283,7 +5300,7 @@ window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
0);
screen_write_stop(&ctx);
wp->flags |= PANE_REDRAWSCROLLBAR;
window_pane_scrollbar_redraw(wp);
}
static void
@@ -6539,6 +6556,7 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
if (ny == 0)
return;
data->oy -= ny;
window_pane_scrollbar_show(wp, 1);
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
@@ -6570,7 +6588,10 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
return;
}
screen_write_start_pane(&ctx, wp, NULL);
if (window_pane_scrollbar_overlay_visible(wp))
screen_write_start(&ctx, &data->screen);
else
screen_write_start_pane(&ctx, wp, NULL);
screen_write_cursormove(&ctx, 0, 0, 0);
screen_write_deleteline(&ctx, ny, 8);
window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny);
@@ -6585,7 +6606,7 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
window_copy_cursor_offset(wme, data->cx, screen_size_x(s)), data->cy,
0);
screen_write_stop(&ctx);
wp->flags |= PANE_REDRAWSCROLLBAR;
window_pane_scrollbar_redraw(wp);
}
static void
@@ -6604,6 +6625,7 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
if (ny == 0)
return;
data->oy += ny;
window_pane_scrollbar_show(wp, 1);
if (data->searchmark != NULL && !data->timeout)
window_copy_search_marks(wme, NULL, data->searchregex, 1);
@@ -6630,7 +6652,10 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
return;
}
screen_write_start_pane(&ctx, wp, NULL);
if (window_pane_scrollbar_overlay_visible(wp))
screen_write_start(&ctx, &data->screen);
else
screen_write_start_pane(&ctx, wp, NULL);
screen_write_cursormove(&ctx, 0, 0, 0);
screen_write_insertline(&ctx, ny, 8);
window_copy_write_lines(wme, &ctx, 0, ny);
@@ -6641,7 +6666,7 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
screen_write_cursormove(&ctx, window_copy_cursor_offset(wme, data->cx,
screen_size_x(s)), data->cy, 0);
screen_write_stop(&ctx);
wp->flags |= PANE_REDRAWSCROLLBAR;
window_pane_scrollbar_redraw(wp);
}
static void

View File

@@ -36,8 +36,8 @@ static void window_customize_key(struct window_mode_entry *,
#define WINDOW_CUSTOMIZE_DEFAULT_FORMAT \
"#{?is_option," \
"#{?option_is_global,,#[reverse](#{option_scope})#[default] }" \
"#[ignore]" \
"#{option_value}#{?option_unit, #{option_unit},}" \
"#[fg=themelightgrey]#[ignore]#{option_value}" \
"#{?option_unit, #{option_unit},}" \
"," \
"#{key}" \
"}"
@@ -472,6 +472,7 @@ window_customize_build_keys(struct window_customize_modedata *data,
expanded = format_expand(ft, filter);
if (!format_true(expanded)) {
free(expanded);
bd = key_bindings_next(kt, bd);
continue;
}
free(expanded);
@@ -740,8 +741,10 @@ window_customize_draw_option(struct window_customize_modedata *data,
if (strcmp(expanded, value) != 0) {
if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy),
0, &grid_default_cell, "This expands to: %s",
expanded))
expanded)) {
free(expanded);
goto out;
}
}
free(expanded);
}
@@ -766,6 +769,15 @@ window_customize_draw_option(struct window_customize_modedata *data,
"EXAMPLE"))
goto out;
}
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_COLOUR)) {
if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1,
&grid_default_cell, "This is a colour option: "))
goto out;
style_apply(&gc, item->oo, name, ft);
if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &gc,
"EXAMPLE"))
goto out;
}
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_STYLE)) {
if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 1,
&grid_default_cell, "This is a style option: "))
@@ -869,15 +881,24 @@ window_customize_height(__unused void *modedata, __unused u_int height)
}
static const char* window_customize_help_lines[] = {
"#[bold] Enter, s #[default]#[acs]x#[default] Set %1 value",
"#[bold] S #[default]#[acs]x#[default] Set global %1 value",
"#[bold] w #[default]#[acs]x#[default] Set window %1 value",
"#[bold] d #[default]#[acs]x#[default] Set to default value",
"#[bold] D #[default]#[acs]x#[default] Set tagged %1s to default value",
"#[bold] u #[default]#[acs]x#[default] Unset an %1",
"#[bold] U #[default]#[acs]x#[default] Unset tagged %1s",
"#[bold] f #[default]#[acs]x#[default] Enter a filter",
"#[bold] v #[default]#[acs]x#[default] Toggle information",
"#[fg=themelightgrey]"
" Enter, s #[#{E:tree-mode-border-style},acs]x#[default] Set %1 value",
"#[fg=themelightgrey]"
" S #[#{E:tree-mode-border-style},acs]x#[default] Set global %1 value",
"#[fg=themelightgrey]"
" w #[#{E:tree-mode-border-style},acs]x#[default] Set window %1 value",
"#[fg=themelightgrey]"
" d #[#{E:tree-mode-border-style},acs]x#[default] Set to default value",
"#[fg=themelightgrey]"
" D #[#{E:tree-mode-border-style},acs]x#[default] Set tagged %1s to default value",
"#[fg=themelightgrey]"
" u #[#{E:tree-mode-border-style},acs]x#[default] Unset an %1",
"#[fg=themelightgrey]"
" U #[#{E:tree-mode-border-style},acs]x#[default] Unset tagged %1s",
"#[fg=themelightgrey]"
" f #[#{E:tree-mode-border-style},acs]x#[default] Enter a filter",
"#[fg=themelightgrey]"
" v #[#{E:tree-mode-border-style},acs]x#[default] Toggle information",
NULL
};
@@ -976,9 +997,9 @@ window_customize_free_item_callback(void *itemdata)
window_customize_destroy(data);
}
static int
static enum prompt_result
window_customize_set_option_callback(struct client *c, void *itemdata,
const char *s, __unused int done)
const char *s, __unused enum prompt_key_result key)
{
struct window_customize_itemdata *item = itemdata;
struct window_customize_modedata *data = item->data;
@@ -990,12 +1011,12 @@ window_customize_set_option_callback(struct client *c, void *itemdata,
int idx = item->idx;
if (s == NULL || *s == '\0' || data->dead)
return (0);
return (PROMPT_CLOSE);
if (item == NULL || !window_customize_check_item(data, item, NULL))
return (0);
return (PROMPT_CLOSE);
o = options_get(oo, name);
if (o == NULL)
return (0);
return (PROMPT_CLOSE);
oe = options_table_entry(o);
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) {
@@ -1017,13 +1038,13 @@ window_customize_set_option_callback(struct client *c, void *itemdata,
mode_tree_draw(data->data);
data->wp->flags |= PANE_REDRAW;
return (0);
return (PROMPT_CLOSE);
fail:
*cause = toupper((u_char)*cause);
status_message_set(c, -1, 1, 0, 0, "%s", cause);
free(cause);
return (0);
return (PROMPT_CLOSE);
}
static void
@@ -1142,10 +1163,10 @@ window_customize_set_option(struct client *c,
new_item->idx = idx;
data->references++;
status_prompt_set(c, NULL, prompt, value,
mode_tree_set_prompt(data->data, c, prompt, value,
PROMPT_TYPE_COMMAND, PROMPT_NOFORMAT,
window_customize_set_option_callback,
window_customize_free_item_callback, new_item,
PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
window_customize_free_item_callback, new_item);
free(prompt);
free(value);
@@ -1190,9 +1211,9 @@ window_customize_reset_option(struct window_customize_modedata *data,
}
}
static int
static enum prompt_result
window_customize_set_command_callback(struct client *c, void *itemdata,
const char *s, __unused int done)
const char *s, __unused enum prompt_key_result key)
{
struct window_customize_itemdata *item = itemdata;
struct window_customize_modedata *data = item->data;
@@ -1201,9 +1222,9 @@ window_customize_set_command_callback(struct client *c, void *itemdata,
char *error;
if (s == NULL || *s == '\0' || data->dead)
return (0);
return (PROMPT_CLOSE);
if (item == NULL || !window_customize_get_key(item, NULL, &bd))
return (0);
return (PROMPT_CLOSE);
pr = cmd_parse_from_string(s, NULL);
switch (pr->status) {
@@ -1220,27 +1241,27 @@ window_customize_set_command_callback(struct client *c, void *itemdata,
mode_tree_draw(data->data);
data->wp->flags |= PANE_REDRAW;
return (0);
return (PROMPT_CLOSE);
fail:
*error = toupper((u_char)*error);
status_message_set(c, -1, 1, 0, 0, "%s", error);
free(error);
return (0);
return (PROMPT_CLOSE);
}
static int
static enum prompt_result
window_customize_set_note_callback(__unused struct client *c, void *itemdata,
const char *s, __unused int done)
const char *s, __unused enum prompt_key_result key)
{
struct window_customize_itemdata *item = itemdata;
struct window_customize_modedata *data = item->data;
struct key_binding *bd;
if (s == NULL || *s == '\0' || data->dead)
return (0);
return (PROMPT_CLOSE);
if (item == NULL || !window_customize_get_key(item, NULL, &bd))
return (0);
return (PROMPT_CLOSE);
free((void *)bd->note);
bd->note = xstrdup(s);
@@ -1249,7 +1270,7 @@ window_customize_set_note_callback(__unused struct client *c, void *itemdata,
mode_tree_draw(data->data);
data->wp->flags |= PANE_REDRAW;
return (0);
return (PROMPT_CLOSE);
}
static void
@@ -1280,10 +1301,10 @@ window_customize_set_key(struct client *c,
new_item->key = key;
data->references++;
status_prompt_set(c, NULL, prompt, value,
mode_tree_set_prompt(data->data, c, prompt, value,
PROMPT_TYPE_COMMAND, PROMPT_NOFORMAT,
window_customize_set_command_callback,
window_customize_free_item_callback, new_item,
PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
window_customize_free_item_callback, new_item);
free(prompt);
free(value);
} else if (strcmp(s, "Note") == 0) {
@@ -1296,11 +1317,11 @@ window_customize_set_key(struct client *c,
new_item->key = key;
data->references++;
status_prompt_set(c, NULL, prompt,
mode_tree_set_prompt(data->data, c, prompt,
(bd->note == NULL ? "" : bd->note),
PROMPT_TYPE_COMMAND, PROMPT_NOFORMAT,
window_customize_set_note_callback,
window_customize_free_item_callback, new_item,
PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
window_customize_free_item_callback, new_item);
free(prompt);
}
}
@@ -1367,17 +1388,17 @@ window_customize_change_each(void *modedata, void *itemdata,
options_push_changes(item->name);
}
static int
static enum prompt_result
window_customize_change_current_callback(__unused struct client *c,
void *modedata, const char *s, __unused int done)
void *modedata, const char *s, __unused enum prompt_key_result key)
{
struct window_customize_modedata *data = modedata;
struct window_customize_itemdata *item;
if (s == NULL || *s == '\0' || data->dead)
return (0);
return (PROMPT_CLOSE);
if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
return (0);
return (PROMPT_CLOSE);
item = mode_tree_get_current(data->data);
switch (data->change) {
@@ -1400,19 +1421,19 @@ window_customize_change_current_callback(__unused struct client *c,
mode_tree_draw(data->data);
data->wp->flags |= PANE_REDRAW;
return (0);
return (PROMPT_CLOSE);
}
static int
static enum prompt_result
window_customize_change_tagged_callback(struct client *c, void *modedata,
const char *s, __unused int done)
const char *s, __unused enum prompt_key_result key)
{
struct window_customize_modedata *data = modedata;
if (s == NULL || *s == '\0' || data->dead)
return (0);
return (PROMPT_CLOSE);
if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
return (0);
return (PROMPT_CLOSE);
mode_tree_each_tagged(data->data, window_customize_change_each, c,
KEYC_NONE, 0);
@@ -1420,7 +1441,7 @@ window_customize_change_tagged_callback(struct client *c, void *modedata,
mode_tree_draw(data->data);
data->wp->flags |= PANE_REDRAW;
return (0);
return (PROMPT_CLOSE);
}
static void
@@ -1474,11 +1495,11 @@ window_customize_key(struct window_mode_entry *wme, struct client *c,
xasprintf(&prompt, "Reset %s to default? ", item->name);
data->references++;
data->change = WINDOW_CUSTOMIZE_RESET;
status_prompt_set(c, NULL, prompt, "",
window_customize_change_current_callback,
window_customize_free_callback, data,
mode_tree_set_prompt(data->data, c, prompt, "",
PROMPT_TYPE_COMMAND,
PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
PROMPT_TYPE_COMMAND);
window_customize_change_current_callback,
window_customize_free_callback, data);
free(prompt);
break;
case 'D':
@@ -1488,11 +1509,11 @@ window_customize_key(struct window_mode_entry *wme, struct client *c,
xasprintf(&prompt, "Reset %u tagged to default? ", tagged);
data->references++;
data->change = WINDOW_CUSTOMIZE_RESET;
status_prompt_set(c, NULL, prompt, "",
window_customize_change_tagged_callback,
window_customize_free_callback, data,
mode_tree_set_prompt(data->data, c, prompt, "",
PROMPT_TYPE_COMMAND,
PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
PROMPT_TYPE_COMMAND);
window_customize_change_tagged_callback,
window_customize_free_callback, data);
free(prompt);
break;
case 'u':
@@ -1505,11 +1526,11 @@ window_customize_key(struct window_mode_entry *wme, struct client *c,
xasprintf(&prompt, "Unset %s? ", item->name);
data->references++;
data->change = WINDOW_CUSTOMIZE_UNSET;
status_prompt_set(c, NULL, prompt, "",
window_customize_change_current_callback,
window_customize_free_callback, data,
mode_tree_set_prompt(data->data, c, prompt, "",
PROMPT_TYPE_COMMAND,
PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
PROMPT_TYPE_COMMAND);
window_customize_change_current_callback,
window_customize_free_callback, data);
free(prompt);
break;
case 'U':
@@ -1519,11 +1540,11 @@ window_customize_key(struct window_mode_entry *wme, struct client *c,
xasprintf(&prompt, "Unset %u tagged? ", tagged);
data->references++;
data->change = WINDOW_CUSTOMIZE_UNSET;
status_prompt_set(c, NULL, prompt, "",
window_customize_change_tagged_callback,
window_customize_free_callback, data,
mode_tree_set_prompt(data->data, c, prompt, "",
PROMPT_TYPE_COMMAND,
PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
PROMPT_TYPE_COMMAND);
window_customize_change_tagged_callback,
window_customize_free_callback, data);
free(prompt);
break;
case 'H':

636
window-switch.c Normal file
View File

@@ -0,0 +1,636 @@
/* $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 <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
static struct screen *window_switch_init(struct window_mode_entry *,
struct cmd_find_state *, struct args *);
static void window_switch_free(struct window_mode_entry *);
static void window_switch_resize(struct window_mode_entry *, u_int,
u_int);
static void window_switch_key(struct window_mode_entry *,
struct client *, struct session *,
struct winlink *, key_code, struct mouse_event *);
static enum prompt_result window_switch_prompt_callback(void *, const char *,
enum prompt_key_result);
#define WINDOW_SWITCH_DEFAULT_COMMAND "switch-client -Zt '%%'"
#define WINDOW_SWITCH_DEFAULT_FORMAT \
"#{?window_format," \
"#{window_name} " \
"#[dim]#{session_name}:#{window_index}#{window_flags}#[default] " \
"#[dim]#{pane_current_command}#[default] " \
"#[dim]#{?#{!=:#{pane_title},#{host_short}},#{pane_title},}#[default]" \
"," \
"#{session_name} " \
"#[dim]#{session_windows} windows#[default] " \
"#{?session_attached,attached,#[dim]detached#[default]} " \
"#[dim]#{window_name}#[default]" \
"}"
const struct window_mode window_switch_mode = {
.name = "switch-mode",
.default_format = WINDOW_SWITCH_DEFAULT_FORMAT,
.init = window_switch_init,
.free = window_switch_free,
.resize = window_switch_resize,
.key = window_switch_key,
};
enum window_switch_type {
WINDOW_SWITCH_TYPE_SESSION,
WINDOW_SWITCH_TYPE_WINDOW
};
struct window_switch_itemdata {
enum window_switch_type type;
int session;
int winlink;
uint64_t tag;
char *text;
bitstr_t *match;
u_int score;
u_int order;
};
struct window_switch_modedata {
struct window_pane *wp;
struct screen screen;
int zoomed;
char *format;
char *command;
enum window_switch_type type;
char *filter;
struct prompt *prompt;
u_int prompt_cx;
struct window_switch_itemdata **item_list;
u_int item_size;
struct window_switch_itemdata **matches;
u_int matches_size;
u_int current;
u_int offset;
};
static void
window_switch_free_item(struct window_switch_itemdata *item)
{
free(item->match);
free(item->text);
free(item);
}
static struct window_switch_itemdata *
window_switch_add_item(struct window_switch_modedata *data)
{
struct window_switch_itemdata *item;
data->item_list = xreallocarray(data->item_list, data->item_size + 1,
sizeof *data->item_list);
item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
return (item);
}
static void
window_switch_add_session(struct window_switch_modedata *data,
struct session *s, u_int *order)
{
struct window_switch_itemdata *item;
struct format_tree *ft;
ft = format_create(NULL, NULL, FORMAT_NONE, 0);
format_defaults(ft, NULL, s, NULL, NULL);
item = window_switch_add_item(data);
item->type = WINDOW_SWITCH_TYPE_SESSION;
item->session = s->id;
item->winlink = -1;
item->tag = (uint64_t)s;
item->order = (*order)++;
item->text = format_expand(ft, data->format);
format_free(ft);
}
static void
window_switch_add_window(struct window_switch_modedata *data,
struct winlink *wl, u_int *order)
{
struct window_switch_itemdata *item;
struct format_tree *ft;
ft = format_create(NULL, NULL, FORMAT_NONE, 0);
format_defaults(ft, NULL, wl->session, wl, NULL);
item = window_switch_add_item(data);
item->type = WINDOW_SWITCH_TYPE_WINDOW;
item->session = wl->session->id;
item->winlink = wl->idx;
item->tag = (uint64_t)wl;
item->order = (*order)++;
item->text = format_expand(ft, data->format);
format_free(ft);
}
static int
window_switch_compare(const void *a0, const void *b0)
{
struct window_switch_itemdata *const *a = a0;
struct window_switch_itemdata *const *b = b0;
if ((*a)->score > (*b)->score)
return (-1);
if ((*a)->score < (*b)->score)
return (1);
if ((*a)->order < (*b)->order)
return (-1);
if ((*a)->order > (*b)->order)
return (1);
return (0);
}
static void
window_switch_build(struct window_switch_modedata *data)
{
struct window_switch_itemdata *item, **m = NULL;
const char *f = data->filter;
u_int ns, nw, i, n = 0, order = 0;
u_int sx = screen_size_x(&data->screen);
struct session **sl;
struct winlink **wl;
struct sort_criteria sort_crit;
sort_crit.order = SORT_NAME;
sort_crit.reversed = 0;
for (i = 0; i < data->item_size; i++)
window_switch_free_item(data->item_list[i]);
free(data->item_list);
data->item_list = NULL;
data->item_size = 0;
switch (data->type) {
case WINDOW_SWITCH_TYPE_SESSION:
sl = sort_get_sessions(&ns, &sort_crit);
for (i = 0; i < ns; i++)
window_switch_add_session(data, sl[i], &order);
break;
case WINDOW_SWITCH_TYPE_WINDOW:
wl = sort_get_winlinks(&nw, &sort_crit);
for (i = 0; i < nw; i++)
window_switch_add_window(data, wl[i], &order);
break;
}
for (i = 0; i < data->item_size; i++) {
item = data->item_list[i];
if (*f == '\0') {
m = xreallocarray(m, n + 1, sizeof *m);
m[n++] = item;
continue;
}
item->match = fuzzy_match(f, item->text, sx, &item->score);
if (item->match == NULL)
continue;
m = xreallocarray(m, n + 1, sizeof *m);
m[n++] = item;
}
qsort(m, n, sizeof *m, window_switch_compare);
free(data->matches);
data->matches = m;
data->matches_size = n;
}
static u_int
window_switch_visible(struct window_switch_modedata *data)
{
u_int sy = screen_size_y(&data->screen);
if (sy <= 1)
return (0);
return (sy - 1);
}
static void
window_switch_set_current(struct window_switch_modedata *data, u_int current)
{
u_int visible = window_switch_visible(data);
if (data->matches_size == 0) {
data->current = 0;
data->offset = 0;
return;
}
if (current > data->matches_size - 1)
current = data->matches_size - 1;
data->current = current;
if (data->current < data->offset)
data->offset = data->current;
else if (visible != 0 && data->current >= data->offset + visible)
data->offset = data->current - visible + 1;
}
static void
window_switch_draw_screen(struct window_mode_entry *wme)
{
struct window_pane *wp = wme->wp;
struct window_switch_modedata *data = wme->data;
struct options *oo = wp->options;
struct screen_write_ctx ctx;
struct screen *s = &data->screen;
u_int sx = screen_size_x(s), i, j;
u_int sy = screen_size_y(s), visible, idx;
struct window_switch_itemdata *item;
struct grid_cell mgc, sgc, gc;
const struct grid_cell *dgc = &grid_default_cell;
struct prompt_draw_data pdd;
screen_write_start(&ctx, s);
screen_write_clearscreen(&ctx, 8);
if (sy <= 1) {
screen_write_stop(&ctx);
return;
}
style_apply(&mgc, oo, "switch-mode-match-style", NULL);
style_apply(&sgc, oo, "mode-style", NULL);
visible = window_switch_visible(data);
for (i = 0; i < visible; i++) {
idx = data->offset + i;
if (idx >= data->matches_size)
break;
item = data->matches[idx];
screen_write_cursormove(&ctx, 0, i, 0);
if (idx != data->current)
format_draw(&ctx, dgc, sx, item->text, NULL, 0);
else {
screen_write_clearendofline(&ctx, sgc.bg);
format_draw(&ctx, &sgc, sx, item->text, NULL, 0);
}
if (item->match == NULL)
continue;
for (j = 0; j < sx; j++) {
if (!bit_test(item->match, j))
continue;
grid_get_cell(s->grid, j, i, &gc);
gc.attr = mgc.attr;
gc.fg = mgc.fg;
gc.bg = mgc.bg;
screen_write_cursormove(&ctx, j, i, 0);
screen_write_cell(&ctx, &gc);
}
}
if (data->prompt != NULL) {
pdd.ctx = &ctx;
pdd.cursor_x = &data->prompt_cx;
pdd.area_x = 0;
pdd.area_width = sx;
pdd.prompt_line = sy - 1;
s->mode |= MODE_CURSOR;
prompt_draw(data->prompt, &pdd);
screen_write_cursormove(&ctx, data->prompt_cx, sy - 1, 0);
}
screen_write_stop(&ctx);
}
static struct screen *
window_switch_init(struct window_mode_entry *wme,
struct cmd_find_state *fs, struct args *args)
{
struct window_pane *wp = wme->wp;
struct window_switch_modedata *data;
struct screen *s;
struct prompt_create_data pd;
wme->data = data = xcalloc(1, sizeof *data);
data->wp = wp;
if (args_has(args, 'w'))
data->type = WINDOW_SWITCH_TYPE_WINDOW;
else
data->type = WINDOW_SWITCH_TYPE_SESSION;
data->filter = xstrdup("");
if (args == NULL || !args_has(args, 'F'))
data->format = xstrdup(WINDOW_SWITCH_DEFAULT_FORMAT);
else
data->format = xstrdup(args_get(args, 'F'));
if (args == NULL || args_count(args) == 0)
data->command = xstrdup(WINDOW_SWITCH_DEFAULT_COMMAND);
else
data->command = xstrdup(args_string(args, 0));
memset(&pd, 0, sizeof pd);
prompt_set_options(&pd, fs->s);
pd.fs = fs;
pd.prompt = "(search) ";
pd.input = "";
pd.type = PROMPT_TYPE_SEARCH;
pd.flags = PROMPT_INCREMENTAL|PROMPT_NOFORMAT|PROMPT_ISMODE|
PROMPT_EDITARROWS;
pd.inputcb = window_switch_prompt_callback;
pd.data = data;
data->prompt = prompt_create(&pd);
prompt_update(data->prompt, "(search) ", data->filter);
if (!args_has(args, 'Z'))
data->zoomed = -1;
else {
data->zoomed = (wp->window->flags & WINDOW_ZOOMED);
if (!data->zoomed && window_zoom(wp) == 0)
server_redraw_window(wp->window);
}
s = &data->screen;
screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
window_switch_build(data);
prompt_incremental_start(data->prompt);
window_switch_draw_screen(wme);
return (s);
}
static void
window_switch_free(struct window_mode_entry *wme)
{
struct window_switch_modedata *data = wme->data;
u_int i;
if (data->zoomed == 0)
server_unzoom_window(wme->wp->window);
for (i = 0; i < data->item_size; i++)
window_switch_free_item(data->item_list[i]);
free(data->item_list);
free(data->matches);
free(data->filter);
prompt_free(data->prompt);
free(data->format);
free(data->command);
screen_free(&data->screen);
free(data);
}
static void
window_switch_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
{
struct window_switch_modedata *data = wme->data;
struct screen *s = &data->screen;
screen_resize(s, sx, sy, 0);
window_switch_build(data);
window_switch_set_current(data, data->current);
window_switch_draw_screen(wme);
}
static int
window_switch_run_command(struct window_switch_modedata *data, struct client *c)
{
struct window_switch_itemdata *item;
struct cmd_find_state fs;
struct session *s;
struct winlink *wl;
char *target = NULL;
struct cmdq_state *state;
char *command, *error;
enum cmd_parse_status status;
if (data->matches_size == 0)
return (0);
item = data->matches[data->current];
cmd_find_clear_state(&fs, 0);
switch (item->type) {
case WINDOW_SWITCH_TYPE_SESSION:
s = session_find_by_id(item->session);
if (s != NULL) {
xasprintf(&target, "=%s:", s->name);
cmd_find_from_session(&fs, s, 0);
}
break;
case WINDOW_SWITCH_TYPE_WINDOW:
s = session_find_by_id(item->session);
if (s != NULL) {
wl = winlink_find_by_index(&s->windows, item->winlink);
if (s != NULL && wl != NULL) {
xasprintf(&target, "=%s:%u.", s->name, wl->idx);
cmd_find_from_winlink(&fs, wl, 0);
}
}
break;
}
if (target == NULL)
return (0);
command = cmd_template_replace(data->command, target, 1);
if (command != NULL && *command != '\0') {
state = cmdq_new_state(&fs, NULL, 0);
status = cmd_parse_and_append(command, NULL, c, state, &error);
if (status == CMD_PARSE_ERROR) {
if (c != NULL) {
*error = toupper((u_char)*error);
status_message_set(c, -1, 1, 0, 0, "%s", error);
}
free(error);
}
cmdq_free_state(state);
}
free(command);
free(target);
return (1);
}
static enum prompt_result
window_switch_prompt_callback(void *arg, const char *s,
enum prompt_key_result key)
{
struct window_switch_modedata *data = arg;
if (key != PROMPT_KEY_HANDLED)
return (PROMPT_CONTINUE);
if (s == NULL)
s = "";
else if (*s != '\0')
s++;
free(data->filter);
data->filter = xstrdup(s);
window_switch_build(data);
data->current = 0;
data->offset = 0;
return (PROMPT_CONTINUE);
}
static void
window_switch_key(struct window_mode_entry *wme, struct client *c,
__unused struct session *s, __unused struct winlink *wl, key_code key,
struct mouse_event *m)
{
struct window_pane *wp = wme->wp;
struct window_switch_modedata *data = wme->data;
u_int visible, current = data->current;
u_int x, y, size = data->matches_size;
enum prompt_key_result result;
int redraw = 0;
if (KEYC_IS_MOUSE(key)) {
if (m == NULL || cmd_mouse_at(wp, m, &x, &y, 0) != 0)
return;
if (data->prompt != NULL && screen_size_y(&data->screen) != 0 &&
y == screen_size_y(&data->screen) - 1 &&
MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_1 && !MOUSE_DRAG(m->b) &&
!MOUSE_RELEASE(m->b)) {
result = prompt_mouse(data->prompt, x, 0,
screen_size_x(&data->screen), &redraw);
if (redraw || result == PROMPT_KEY_HANDLED) {
window_switch_draw_screen(wme);
wp->flags |= PANE_REDRAW;
}
return;
}
switch (key) {
case KEYC_WHEELUP_PANE:
if (size != 0 && current != 0)
window_switch_set_current(data, current - 1);
goto moved;
case KEYC_WHEELDOWN_PANE:
if (size != 0 && current != size - 1)
window_switch_set_current(data, current + 1);
goto moved;
case KEYC_MOUSEDOWN1_PANE:
case KEYC_DOUBLECLICK1_PANE:
if (y >= window_switch_visible(data) ||
data->offset + y >= size)
return;
window_switch_set_current(data, data->offset + y);
if (key == KEYC_DOUBLECLICK1_PANE) {
if (window_switch_run_command(data, c))
window_pane_reset_mode(wp);
return;
}
goto moved;
}
return;
}
switch (key) {
case 'p'|KEYC_CTRL:
case 'k'|KEYC_CTRL:
key = KEYC_UP;
break;
case 'n'|KEYC_CTRL:
case 'j'|KEYC_CTRL:
key = KEYC_DOWN;
break;
}
switch (key) {
case '\r':
if (window_switch_run_command(data, c))
window_pane_reset_mode(wp);
return;
case '\033': /* Escape */
case '['|KEYC_CTRL:
case 'c'|KEYC_CTRL:
case 'g'|KEYC_CTRL:
window_pane_reset_mode(wp);
return;
}
if (data->prompt != NULL) {
result = prompt_key(data->prompt, key, &redraw);
if (redraw) {
window_switch_draw_screen(wme);
wp->flags |= PANE_REDRAW;
}
if (result == PROMPT_KEY_HANDLED ||
result == PROMPT_KEY_NOT_HANDLED)
return;
current = data->current;
size = data->matches_size;
}
switch (key) {
case KEYC_UP:
if (size == 0)
goto moved;
if (current == 0)
window_switch_set_current(data, size - 1);
else
window_switch_set_current(data, current - 1);
goto moved;
case KEYC_DOWN:
if (size == 0)
goto moved;
if (current == size - 1)
window_switch_set_current(data, 0);
else
window_switch_set_current(data, current + 1);
goto moved;
case KEYC_PPAGE:
visible = window_switch_visible(data);
if (current >= visible)
window_switch_set_current(data, current - visible);
else
window_switch_set_current(data, 0);
goto moved;
case KEYC_NPAGE:
visible = window_switch_visible(data);
window_switch_set_current(data, current + visible);
goto moved;
case KEYC_HOME:
window_switch_set_current(data, 0);
goto moved;
case KEYC_END:
if (size > 0)
window_switch_set_current(data, size - 1);
goto moved;
}
moved:
window_switch_draw_screen(wme);
wp->flags |= PANE_REDRAW;
}

View File

@@ -38,15 +38,15 @@ static void window_tree_key(struct window_mode_entry *,
#define WINDOW_TREE_DEFAULT_FORMAT \
"#{?pane_format," \
"#{?pane_marked,#[reverse],}#{?pane_floating_flag,#[italics],}" \
"#{pane_current_command}#{pane_flags}" \
"#{?pane_marked,#[fg=thememagenta],}#{?pane_floating_flag,#[underscore],}" \
"#{pane_current_command}#[fg=themelightgrey]#{pane_flags}" \
"#{?#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}},: \"#{pane_title}\",}" \
",window_format," \
"#{?window_marked_flag,#[reverse],}" \
"#{window_name}#{window_flags}" \
"#{?window_marked_flag,#[fg=thememagenta],}" \
"#{window_name}#[fg=themelightgrey]#{window_flags}" \
"#{?#{&&:#{==:#{window_panes},1},#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}}},: \"#{pane_title}\",}" \
"," \
"#{session_windows} windows" \
"#[fg=themelightgrey]#{session_windows} windows" \
"#{?session_grouped, " \
"(group #{session_group}: " \
"#{session_group_list})," \
@@ -114,6 +114,7 @@ struct window_tree_modedata {
char *command;
int squash_groups;
int hide_preview_this_pane;
int preview_is_info;
int prompt_flags;
struct window_tree_itemdata **item_list;
@@ -141,6 +142,80 @@ static enum sort_order window_tree_order_seq[] = {
SORT_END,
};
#define WINDOW_TREE_FLAG(label, cond) \
"#{?" cond ",#[fg=themegreen],#[fg=themelightgrey]}" label "#[default]"
static const char *window_tree_pane_info_lines[] = {
"#[fg=themelightgrey]Pane #[#{E:tree-mode-border-style},acs]x#[default] "
"#{pane_index} #[fg=themelightgrey](#{pane_id})#[default]",
"#[fg=themelightgrey]Title #[#{E:tree-mode-border-style},acs]x#[default] "
"#{pane_title}",
"#[fg=themelightgrey]Command #[#{E:tree-mode-border-style},acs]x#[default] "
"#{pane_current_command} #[fg=themelightgrey](PID #{pane_pid})#[default]",
"#[fg=themelightgrey]Path #[#{E:tree-mode-border-style},acs]x#[default] "
"#{pane_current_path}",
"#[fg=themelightgrey]TTY #[#{E:tree-mode-border-style},acs]x#[default] "
"#{pane_tty}",
"#[fg=themelightgrey]Position #[#{E:tree-mode-border-style},acs]x#[default] "
"#{pane_x},#{pane_y} #{pane_width}x#{pane_height}",
"#[fg=themelightgrey]Mode #[#{E:tree-mode-border-style},acs]x#[default] "
"#{?pane_in_mode,#{pane_mode},none}",
"#[fg=themelightgrey]Flags #[#{E:tree-mode-border-style},acs]x#[default] "
WINDOW_TREE_FLAG("active", "pane_active") " "
WINDOW_TREE_FLAG("zoomed", "window_zoomed_flag") " "
WINDOW_TREE_FLAG("marked", "pane_marked") " "
WINDOW_TREE_FLAG("sync", "pane_synchronized") " "
WINDOW_TREE_FLAG("dead", "pane_dead") " "
WINDOW_TREE_FLAG("piped", "pane_pipe"),
};
static const char *window_tree_window_info_lines[] = {
"#[fg=themelightgrey]Window #[#{E:tree-mode-border-style},acs]x#[default] "
"#{window_index}: #{window_name} #[fg=themelightgrey](#{window_id})#[default]",
"#[fg=themelightgrey]Size #[#{E:tree-mode-border-style},acs]x#[default] "
"#{window_width}x#{window_height}",
"#[fg=themelightgrey]Panes #[#{E:tree-mode-border-style},acs]x#[default] "
"#{window_panes}",
"#[fg=themelightgrey]Activity Time #[#{E:tree-mode-border-style},acs]x#[default] "
"#{t:window_activity} #[fg=themelightgrey](#{t/r:window_activity})#[default]",
"#[fg=themelightgrey]Sessions #[#{E:tree-mode-border-style},acs]x#[default] "
"#{s/,/ /:window_linked_sessions_list}",
"#[fg=themelightgrey]Flags #[#{E:tree-mode-border-style},acs]x#[default] "
WINDOW_TREE_FLAG("active", "window_active") " "
WINDOW_TREE_FLAG("last", "window_last_flag") " "
WINDOW_TREE_FLAG("bell", "window_bell_flag") " "
WINDOW_TREE_FLAG("activity", "window_activity_flag") " "
WINDOW_TREE_FLAG("silence", "window_silence_flag") " "
WINDOW_TREE_FLAG("zoomed", "window_zoomed_flag") " "
WINDOW_TREE_FLAG("marked", "window_marked_flag"),
};
static const char *window_tree_session_info_lines[] = {
"#[fg=themelightgrey]Session #[#{E:tree-mode-border-style},acs]x#[default] "
"#{session_name} #[fg=themelightgrey](#{session_id})#[default]",
"#[fg=themelightgrey]Created Time #[#{E:tree-mode-border-style},acs]x#[default] "
"#{t:session_created} #[fg=themelightgrey](#{t/r:session_created})#[default]",
"#[fg=themelightgrey]Activity Time #[#{E:tree-mode-border-style},acs]x#[default] "
"#{t:session_activity} #[fg=themelightgrey](#{t/r:session_activity})#[default]",
"#[fg=themelightgrey]Attached Time #[#{E:tree-mode-border-style},acs]x#[default] "
"#{?#{t:session_last_attached},#{t:session_last_attached} #[fg=themelightgrey](#{t/r:session_last_attached})#[default],never}",
"#[fg=themelightgrey]Clients #[#{E:tree-mode-border-style},acs]x#[default] "
"#{s/,/ /:session_attached_list}",
"#[fg=themelightgrey]Windows #[#{E:tree-mode-border-style},acs]x#[default] "
"#{session_windows}",
"#[fg=themelightgrey]Path #[#{E:tree-mode-border-style},acs]x#[default] "
"#{session_path}",
"#[fg=themelightgrey]Group #[#{E:tree-mode-border-style},acs]x#[default] "
"#{?session_grouped,#{session_group} (#{session_group_size}),none}",
"#[fg=themelightgrey]Flags #[#{E:tree-mode-border-style},acs]x#[default] "
WINDOW_TREE_FLAG("attached", "session_attached") " "
WINDOW_TREE_FLAG("grouped", "session_grouped") " "
WINDOW_TREE_FLAG("marked", "session_marked") " "
WINDOW_TREE_FLAG("bell", "session_bell_flag") " "
WINDOW_TREE_FLAG("activity", "session_activity_flag") " "
WINDOW_TREE_FLAG("silence", "session_silence_flag"),
};
static void
window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp,
struct winlink **wlp, struct window_pane **wp)
@@ -401,7 +476,8 @@ window_tree_build(void *modedata, struct sort_criteria *sort_crit,
static void
window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py,
u_int sx, u_int sy, const struct grid_cell *gc, const char *label)
u_int sx, u_int sy, const struct grid_cell *border_gc,
const struct grid_cell *label_gc, const char *label)
{
u_int width, ox, oy;
char *new_label = NULL;
@@ -420,14 +496,22 @@ window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py,
screen_write_cursormove(ctx, px + ox - 2, py + oy - 1, 0);
screen_write_box(ctx, width + 4, 3, BOX_LINES_DEFAULT,
NULL, NULL);
border_gc, NULL);
screen_write_cursormove(ctx, px + ox - 1, py + oy, 0);
screen_write_clearcharacter(ctx, width + 2, 8);
screen_write_clearcharacter(ctx, width + 2, border_gc->bg);
screen_write_cursormove(ctx, px + ox, py + oy, 0);
format_draw(ctx, gc, width, label, NULL, 0);
format_draw(ctx, label_gc, width, label, NULL, 0);
free(new_label);
}
static void
window_tree_border_cell(struct grid_cell *gc, struct options *oo,
struct format_tree *ft)
{
memcpy(gc, &grid_default_cell, sizeof *gc);
style_apply(gc, oo, "tree-mode-border-style", ft);
}
static void
window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
struct screen_write_ctx *ctx, u_int sx, u_int sy)
@@ -437,7 +521,7 @@ window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
u_int cx = ctx->s->cx, cy = ctx->s->cy;
u_int loop, total, visible, each, width, offset;
u_int current, start, end, remaining, i;
struct grid_cell gc;
struct grid_cell gc, label_gc;
int left, right;
char *label;
const char *format;
@@ -495,20 +579,21 @@ window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
if (each == 0)
return;
window_tree_border_cell(&gc, data->wp->window->options, NULL);
if (left) {
data->left = cx + 2;
screen_write_cursormove(ctx, cx + 2, cy, 0);
screen_write_vline(ctx, sy, 0, 0);
screen_write_vline(ctx, sy, 0, 0, &gc);
screen_write_cursormove(ctx, cx, cy + sy / 2, 0);
screen_write_puts(ctx, &grid_default_cell, "<");
screen_write_puts(ctx, &gc, "<");
} else
data->left = -1;
if (right) {
data->right = cx + sx - 3;
screen_write_cursormove(ctx, cx + sx - 3, cy, 0);
screen_write_vline(ctx, sy, 0, 0);
screen_write_vline(ctx, sy, 0, 0, &gc);
screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0);
screen_write_puts(ctx, &grid_default_cell, ">");
screen_write_puts(ctx, &gc, ">");
} else
data->right = -1;
@@ -530,8 +615,10 @@ window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
ft = format_create(NULL, NULL, FORMAT_WINDOW|w->id, 0);
format_defaults(ft, NULL, s, wl, NULL);
memcpy(&gc, &grid_default_cell, sizeof gc);
style_apply(&gc, oo, "tree-mode-preview-style", ft);
window_tree_border_cell(&gc, oo, ft);
memcpy(&label_gc, &grid_default_cell, sizeof label_gc);
style_apply(&label_gc, oo, "tree-mode-preview-style", ft);
label_gc.bg = gc.bg;
if (left)
offset = 3 + (i * each);
@@ -550,7 +637,7 @@ window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
label = format_expand(ft, format);
if (*label != '\0') {
window_tree_draw_label(ctx, cx + offset, cy,
width, sy, &gc, label);
width, sy, &gc, &label_gc, label);
}
free(label);
}
@@ -558,7 +645,7 @@ window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
if (loop != end - 1) {
screen_write_cursormove(ctx, cx + offset + width, cy,
0);
screen_write_vline(ctx, sy, 0, 0);
screen_write_vline(ctx, sy, 0, 0, &gc);
}
loop++;
@@ -575,7 +662,7 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
u_int cx = ctx->s->cx, cy = ctx->s->cy;
u_int loop, total, visible, each, width, offset;
u_int current, start, end, remaining, i;
struct grid_cell gc;
struct grid_cell gc, label_gc;
int left, right;
char *label;
const char *format;
@@ -639,20 +726,21 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
if (each == 0)
return;
window_tree_border_cell(&gc, data->wp->window->options, NULL);
if (left) {
data->left = cx + 2;
screen_write_cursormove(ctx, cx + 2, cy, 0);
screen_write_vline(ctx, sy, 0, 0);
screen_write_vline(ctx, sy, 0, 0, &gc);
screen_write_cursormove(ctx, cx, cy + sy / 2, 0);
screen_write_puts(ctx, &grid_default_cell, "<");
screen_write_puts(ctx, &gc, "<");
} else
data->left = -1;
if (right) {
data->right = cx + sx - 3;
screen_write_cursormove(ctx, cx + sx - 3, cy, 0);
screen_write_vline(ctx, sy, 0, 0);
screen_write_vline(ctx, sy, 0, 0, &gc);
screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0);
screen_write_puts(ctx, &grid_default_cell, ">");
screen_write_puts(ctx, &gc, ">");
} else
data->right = -1;
@@ -675,8 +763,10 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
ft = format_create(NULL, NULL, FORMAT_PANE|wp->id, 0);
format_defaults(ft, NULL, s, wl, wp);
memcpy(&gc, &grid_default_cell, sizeof gc);
style_apply(&gc, oo, "tree-mode-preview-style", ft);
window_tree_border_cell(&gc, oo, ft);
memcpy(&label_gc, &grid_default_cell, sizeof label_gc);
style_apply(&label_gc, oo, "tree-mode-preview-style", ft);
label_gc.bg = gc.bg;
if (left)
offset = 3 + (i * each);
@@ -695,7 +785,7 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
label = format_expand(ft, format);
if (*label != '\0') {
window_tree_draw_label(ctx, cx + offset, cy,
width, sy, &gc, label);
width, sy, &gc, &label_gc, label);
}
free(label);
}
@@ -703,7 +793,7 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
if (loop != end - 1) {
screen_write_cursormove(ctx, cx + offset + width, cy,
0);
screen_write_vline(ctx, sy, 0, 0);
screen_write_vline(ctx, sy, 0, 0, &gc);
}
loop++;
@@ -711,6 +801,81 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
}
}
static void
window_tree_draw_info(struct window_tree_modedata *data, void *itemdata,
struct screen_write_ctx *ctx, u_int sx, u_int sy)
{
struct window_tree_itemdata *item = itemdata;
struct screen *s = ctx->s;
struct session *sp;
struct winlink *wl;
struct window_pane *wp;
struct grid_cell gc;
u_int cx = s->cx, cy = s->cy, i, j, k;
struct format_tree *ft;
char *expanded;
const char *const *lines[3];
u_int count[3], n = 0;
window_tree_pull_item(item, &sp, &wl, &wp);
if (sp == NULL || wp == NULL)
return;
if (item->type == WINDOW_TREE_PANE) {
lines[n] = window_tree_pane_info_lines;
count[n++] = nitems(window_tree_pane_info_lines);
}
if (item->type == WINDOW_TREE_PANE ||
item->type == WINDOW_TREE_WINDOW) {
lines[n] = window_tree_window_info_lines;
count[n++] = nitems(window_tree_window_info_lines);
}
lines[n] = window_tree_session_info_lines;
count[n++] = nitems(window_tree_session_info_lines);
ft = format_create(NULL, NULL, FORMAT_NONE, 0);
format_defaults(ft, NULL, sp, wl, wp);
i = 0;
for (j = 0; j < n; j++) {
if (j != 0) {
if (i == sy)
break;
window_tree_border_cell(&gc, data->wp->window->options,
NULL);
screen_write_cursormove(ctx, cx, cy + i, 0);
screen_write_hline(ctx, sx, 0, 0, BOX_LINES_DEFAULT,
&gc);
if (sx > 14) {
gc.attr |= GRID_ATTR_CHARSET;
screen_write_cursormove(ctx, cx + 14, cy + i,
0);
screen_write_putc(ctx, &gc, 'n');
}
i++;
}
for (k = 0; k < count[j]; k++) {
if (i == sy)
break;
expanded = format_expand(ft, lines[j][k]);
screen_write_cursormove(ctx, cx, cy + i, 0);
format_draw(ctx, &grid_default_cell, sx, expanded, NULL,
0);
free(expanded);
i++;
}
if (i == sy)
break;
}
if (sx > 14 && i < sy) {
window_tree_border_cell(&gc, data->wp->window->options, NULL);
screen_write_cursormove(ctx, cx + 14, cy + i, 0);
screen_write_vline(ctx, sy - i, 0, 0, &gc);
}
format_free(ft);
}
static void
window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx,
u_int sx, u_int sy)
@@ -725,6 +890,11 @@ window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx,
if (wp == NULL)
return;
if (data->preview_is_info) {
window_tree_draw_info(data, item, ctx, sx, sy);
return;
}
switch (item->type) {
case WINDOW_TREE_NONE:
break;
@@ -889,18 +1059,32 @@ window_tree_sort(struct sort_criteria *sort_crit)
}
static const char* window_tree_help_lines[] = {
"#[bold] Enter #[default]#[acs]x#[default] Choose selected item",
"#[bold] S-Up #[default]#[acs]x#[default] Swap current and previous window",
"#[bold] S-Down #[default]#[acs]x#[default] Swap current and next window",
"#[bold] x #[default]#[acs]x#[default] Kill selected item",
"#[bold] X #[default]#[acs]x#[default] Kill tagged items",
"#[bold] < #[default]#[acs]x#[default] Scroll previews left",
"#[bold] > #[default]#[acs]x#[default] Scroll previews right",
"#[bold] m #[default]#[acs]x#[default] Set the marked pane",
"#[bold] M #[default]#[acs]x#[default] Clear the marked pane",
"#[bold] : #[default]#[acs]x#[default] Run a command for each tagged item",
"#[bold] f #[default]#[acs]x#[default] Enter a format",
"#[bold] H #[default]#[acs]x#[default] Jump to the starting pane",
"#[fg=themelightgrey]"
" Enter #[#{E:tree-mode-border-style},acs]x#[default] Choose selected item",
"#[fg=themelightgrey]"
" S-Up #[#{E:tree-mode-border-style},acs]x#[default] Swap current and previous window",
"#[fg=themelightgrey]"
" S-Down #[#{E:tree-mode-border-style},acs]x#[default] Swap current and next window",
"#[fg=themelightgrey]"
" x #[#{E:tree-mode-border-style},acs]x#[default] Kill selected item",
"#[fg=themelightgrey]"
" X #[#{E:tree-mode-border-style},acs]x#[default] Kill tagged items",
"#[fg=themelightgrey]"
" < #[#{E:tree-mode-border-style},acs]x#[default] Scroll previews left",
"#[fg=themelightgrey]"
" > #[#{E:tree-mode-border-style},acs]x#[default] Scroll previews right",
"#[fg=themelightgrey]"
" m #[#{E:tree-mode-border-style},acs]x#[default] Set the marked pane",
"#[fg=themelightgrey]"
" M #[#{E:tree-mode-border-style},acs]x#[default] Clear the marked pane",
"#[fg=themelightgrey]"
" i #[#{E:tree-mode-border-style},acs]x#[default] Toggle session, window and pane information",
"#[fg=themelightgrey]"
" : #[#{E:tree-mode-border-style},acs]x#[default] Run a command for each tagged item",
"#[fg=themelightgrey]"
" f #[#{E:tree-mode-border-style},acs]x#[default] Enter a format",
"#[fg=themelightgrey]"
" H #[#{E:tree-mode-border-style},acs]x#[default] Jump to the starting pane",
NULL
};
@@ -954,6 +1138,7 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
window_tree_get_key, window_tree_swap, window_tree_sort,
window_tree_help, data, window_tree_menu_items, &s);
mode_tree_zoom(data->data, args);
mode_tree_view_name(data->data, "preview");
mode_tree_build(data->data);
mode_tree_draw(data->data);
@@ -1080,14 +1265,14 @@ window_tree_command_done(__unused struct cmdq_item *item, void *modedata)
return (CMD_RETURN_NORMAL);
}
static int
static enum prompt_result
window_tree_command_callback(struct client *c, void *modedata, const char *s,
__unused int done)
__unused enum prompt_key_result key)
{
struct window_tree_modedata *data = modedata;
if (s == NULL || *s == '\0' || data->dead)
return (0);
return (PROMPT_CLOSE);
data->entered = s;
mode_tree_each_tagged(data->data, window_tree_command_each, c,
@@ -1097,7 +1282,7 @@ window_tree_command_callback(struct client *c, void *modedata, const char *s,
data->references++;
cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
return (0);
return (PROMPT_CLOSE);
}
static void
@@ -1139,17 +1324,17 @@ window_tree_kill_each(__unused void *modedata, void *itemdata,
}
}
static int
static enum prompt_result
window_tree_kill_current_callback(struct client *c, void *modedata,
const char *s, __unused int done)
const char *s, __unused enum prompt_key_result key)
{
struct window_tree_modedata *data = modedata;
struct mode_tree_data *mtd = data->data;
if (s == NULL || *s == '\0' || data->dead)
return (0);
return (PROMPT_CLOSE);
if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
return (0);
return (PROMPT_CLOSE);
window_tree_kill_each(data, mode_tree_get_current(mtd), c, KEYC_NONE);
server_renumber_all();
@@ -1157,20 +1342,20 @@ window_tree_kill_current_callback(struct client *c, void *modedata,
data->references++;
cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
return (0);
return (PROMPT_CLOSE);
}
static int
static enum prompt_result
window_tree_kill_tagged_callback(struct client *c, void *modedata,
const char *s, __unused int done)
const char *s, __unused enum prompt_key_result key)
{
struct window_tree_modedata *data = modedata;
struct mode_tree_data *mtd = data->data;
if (s == NULL || *s == '\0' || data->dead)
return (0);
return (PROMPT_CLOSE);
if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
return (0);
return (PROMPT_CLOSE);
mode_tree_each_tagged(mtd, window_tree_kill_each, c, KEYC_NONE, 1);
server_renumber_all();
@@ -1178,7 +1363,7 @@ window_tree_kill_tagged_callback(struct client *c, void *modedata,
data->references++;
cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
return (0);
return (PROMPT_CLOSE);
}
static key_code
@@ -1293,6 +1478,13 @@ again:
server_clear_marked();
mode_tree_build(data->data);
break;
case 'i':
data->preview_is_info = !data->preview_is_info;
if (data->preview_is_info)
mode_tree_view_name(data->data, "info");
else
mode_tree_view_name(data->data, "preview");
break;
case 'x':
window_tree_pull_item(item, &ns, &nwl, &nwp);
switch (item->type) {
@@ -1317,10 +1509,11 @@ again:
if (prompt == NULL)
break;
data->references++;
status_prompt_set(c, NULL, prompt, "",
mode_tree_set_prompt(data->data, c, prompt, "",
PROMPT_TYPE_COMMAND,
PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
window_tree_kill_current_callback, window_tree_command_free,
data, PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
PROMPT_TYPE_COMMAND);
data);
free(prompt);
break;
case 'X':
@@ -1329,10 +1522,11 @@ again:
break;
xasprintf(&prompt, "Kill %u tagged? ", tagged);
data->references++;
status_prompt_set(c, NULL, prompt, "",
mode_tree_set_prompt(data->data, c, prompt, "",
PROMPT_TYPE_COMMAND,
PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
window_tree_kill_tagged_callback, window_tree_command_free,
data, PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
PROMPT_TYPE_COMMAND);
data);
free(prompt);
break;
case ':':
@@ -1342,9 +1536,10 @@ again:
else
xasprintf(&prompt, "(current) ");
data->references++;
status_prompt_set(c, NULL, prompt, "",
mode_tree_set_prompt(data->data, c, prompt, "",
PROMPT_TYPE_COMMAND, PROMPT_NOFORMAT,
window_tree_command_callback, window_tree_command_free,
data, PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
data);
free(prompt);
break;
case '\r':

View File

@@ -55,7 +55,7 @@ window_visible_ranges(struct window_pane *base_wp, int px, int py, u_int width,
struct window *w;
struct visible_range *ri;
static struct visible_ranges sr = { NULL, 0, 0 };
int found_self, sb, sb_w, sb_pos;
int found_self, sb_w, sb_pos;
int lb, rb, tb, bb, sx, ex, no_border;
u_int i, s;
@@ -95,8 +95,6 @@ window_visible_ranges(struct window_pane *base_wp, int px, int py, u_int width,
r->used = 1;
}
sb = options_get_number(w->options, "pane-scrollbars");
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
found_self = 0;
TAILQ_FOREACH_REVERSE(wp, &w->z_index, window_panes_zindex, zentry) {
@@ -126,7 +124,9 @@ window_visible_ranges(struct window_pane *base_wp, int px, int py, u_int width,
continue;
sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
if (!window_pane_show_scrollbar(wp, sb))
if (window_pane_scrollbar_reserve(wp))
sb_pos = w->sb_pos;
else
sb_w = sb_pos = 0;
for (i = 0; i < r->used; i++) {

352
window.c
View File

@@ -71,6 +71,7 @@ struct window_pane_input_data {
static struct window_pane *window_pane_create(struct window *, u_int, u_int,
u_int);
static void window_pane_destroy(struct window_pane *);
static void window_pane_scrollbar_timer(int, short, void *);
static void window_pane_full_size_offset(struct window_pane *wp,
int *xoff, int *yoff, u_int *sx, u_int *sy);
@@ -78,6 +79,14 @@ RB_GENERATE(windows, window, entry, window_cmp);
RB_GENERATE(winlinks, winlink, entry, winlink_cmp);
RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp);
struct window_pane_prompt {
u_int wp_id;
struct client *c;
status_prompt_input_cb inputcb;
prompt_free_cb freecb;
void *data;
};
int
window_cmp(struct window *w1, struct window *w2)
{
@@ -411,11 +420,11 @@ window_remove_ref(struct window *w, const char *from)
}
void
window_set_name(struct window *w, const char *new_name, const char *forbid)
window_set_name(struct window *w, const char *new_name, int untrusted)
{
char *name;
name = clean_name(new_name, forbid);
name = clean_name(new_name, untrusted);
if (name != NULL) {
free(w->name);
w->name = name;
@@ -635,6 +644,7 @@ window_redraw_active_switch(struct window *w, struct window_pane *wp)
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
wp->flags |= PANE_REDRAW;
redraw_invalidate_scene(w);
}
wp = w->active;
@@ -772,6 +782,7 @@ window_zoom(struct window_pane *wp)
w->flags |= WINDOW_ZOOMED;
notify_window("window-layout-changed", w);
redraw_invalidate_scene(w);
return (0);
}
@@ -798,6 +809,7 @@ window_unzoom(struct window *w, int notify)
if (notify)
notify_window("window-layout-changed", w);
redraw_invalidate_scene(w);
return (0);
}
@@ -854,6 +866,7 @@ window_add_pane(struct window *w, struct window_pane *other, u_int hlimit,
else {
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
}
redraw_invalidate_scene(w);
return (wp);
}
@@ -895,6 +908,7 @@ window_lost_pane(struct window *w, struct window_pane *wp)
notify_window("window-pane-changed", w);
window_update_focus(w);
}
redraw_invalidate_scene(w);
}
void
@@ -903,6 +917,7 @@ window_remove_pane(struct window *w, struct window_pane *wp)
window_lost_pane(w, wp);
TAILQ_REMOVE(&w->panes, wp, entry);
TAILQ_REMOVE(&w->z_index, wp, zentry);
redraw_invalidate_scene(w);
window_pane_destroy(wp);
}
@@ -1126,6 +1141,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
screen_init(&wp->status_screen, 1, 1, 0);
style_ranges_init(&wp->border_status_line.ranges);
evtimer_set(&wp->sb_auto_timer, window_pane_scrollbar_timer, wp);
if (gethostname(host, sizeof host) == 0)
screen_set_title(&wp->base, host, 0);
@@ -1157,13 +1173,71 @@ window_pane_wait_finish(struct window_pane *wp)
cmdq_continue(item);
}
static void
window_pane_free_modes(struct window_pane *wp)
{
struct window_mode_entry *wme;
while (!TAILQ_EMPTY(&wp->modes)) {
wme = TAILQ_FIRST(&wp->modes);
TAILQ_REMOVE(&wp->modes, wme, entry);
wme->mode->free(wme);
free(wme);
}
wp->screen = &wp->base;
}
static void
window_pane_scrollbar_timer(__unused int fd, __unused short events, void *arg)
{
struct window_pane *wp = arg;
wp->sb_auto_hover = 0;
window_pane_scrollbar_hide(wp);
}
static int
window_pane_scrollbar_auto_hide(struct window_pane *wp)
{
return (wp->window->sb == PANE_SCROLLBARS_MODAL ||
wp->window->sb == PANE_SCROLLBARS_AUTOHIDE);
}
int
window_pane_scrollbar_overlay_visible(struct window_pane *wp)
{
return (window_pane_scrollbar_overlay(wp) &&
window_pane_scrollbar_visible(wp));
}
void
window_pane_scrollbar_redraw(struct window_pane *wp)
{
if (window_pane_scrollbar_overlay_visible(wp)) {
wp->flags |= PANE_REDRAW;
return;
}
wp->flags |= PANE_REDRAWSCROLLBAR;
}
static void
window_pane_scrollbar_redraw_visibility(struct window_pane *wp)
{
redraw_invalidate_scene(wp->window);
wp->flags |= PANE_REDRAW;
server_redraw_window(wp->window);
}
static void
window_pane_destroy(struct window_pane *wp)
{
window_pane_wait_finish(wp);
spawn_editor_finish(wp);
window_pane_reset_mode_all(wp);
window_pane_clear_prompt(wp);
window_pane_free_modes(wp);
free(wp->searchstr);
if (wp->fd != -1) {
@@ -1190,6 +1264,8 @@ window_pane_destroy(struct window_pane *wp)
event_del(&wp->resize_timer);
if (event_initialized(&wp->sync_timer))
event_del(&wp->sync_timer);
if (event_initialized(&wp->sb_auto_timer))
event_del(&wp->sb_auto_timer);
window_pane_clear_resizes(wp, NULL);
RB_REMOVE(window_pane_tree, &all_window_panes, wp);
@@ -1259,7 +1335,8 @@ window_pane_set_event(struct window_pane *wp)
}
void
window_pane_clear_resizes(struct window_pane *wp, struct window_pane_resize *except)
window_pane_clear_resizes(struct window_pane *wp,
struct window_pane_resize *except)
{
struct window_pane_resize *r, *r1;
@@ -1379,6 +1456,7 @@ window_pane_reset_mode(struct window_pane *wp)
server_kill_pane(wp);
}
/* Reset all modes. */
void
window_pane_reset_mode_all(struct window_pane *wp)
{
@@ -1386,6 +1464,165 @@ window_pane_reset_mode_all(struct window_pane *wp)
window_pane_reset_mode(wp);
}
/* Prompt input callback. */
static enum prompt_result
window_pane_prompt_input_callback(void *data, const char *s,
enum prompt_key_result key)
{
struct window_pane_prompt *wpp = data;
if (wpp->inputcb != NULL)
return (wpp->inputcb(wpp->c, wpp->data, s, key));
return (PROMPT_CLOSE);
}
/* Prompt free callback. */
static void
window_pane_prompt_free_callback(void *data)
{
struct window_pane_prompt *wpp = data;
struct window_pane *wp;
wp = window_pane_find_by_id(wpp->wp_id);
if (wp != NULL && wp->prompt_data == wpp)
wp->prompt_data = NULL;
if (wpp->freecb != NULL)
wpp->freecb(wpp->data);
free(wpp);
}
/* Open a prompt owned by a pane, drawn over the pane instead of the status. */
void
window_pane_set_prompt(struct window_pane *wp, struct client *c,
struct cmd_find_state *fs, const char *msg, const char *input,
status_prompt_input_cb inputcb, prompt_free_cb freecb, void *data,
int flags, enum prompt_type type)
{
struct session *s = NULL;
struct prompt_create_data pd;
struct window_pane_prompt *wpp;
if (c != NULL)
s = c->session;
window_pane_clear_prompt(wp);
wpp = xcalloc(1, sizeof *wpp);
wpp->wp_id = wp->id;
wpp->c = c;
wpp->inputcb = inputcb;
wpp->freecb = freecb;
wpp->data = data;
memset(&pd, 0, sizeof pd);
prompt_set_options(&pd, s);
pd.fs = fs;
pd.prompt = msg;
pd.input = input;
pd.type = type;
pd.flags = flags;
pd.inputcb = window_pane_prompt_input_callback;
pd.freecb = window_pane_prompt_free_callback;
pd.data = wpp;
wp->prompt = prompt_create(&pd);
wp->prompt_data = wpp;
wp->flags |= PANE_REDRAW;
prompt_incremental_start(wp->prompt);
}
/* Close a pane prompt. */
void
window_pane_clear_prompt(struct window_pane *wp)
{
struct prompt *prompt = wp->prompt;
if (prompt == NULL)
return;
wp->prompt = NULL;
prompt_free(prompt);
wp->flags |= PANE_REDRAW;
}
/* Does this pane have an open prompt? */
int
window_pane_has_prompt(struct window_pane *wp)
{
return (wp->prompt != NULL);
}
/* Replace the message and input of an open pane prompt. */
void
window_pane_update_prompt(struct window_pane *wp, const char *msg,
const char *input)
{
if (wp->prompt != NULL) {
prompt_update(wp->prompt, msg, input);
wp->flags |= PANE_REDRAW;
}
}
/*
* Pass a key to a pane prompt. The client is set transiently for the duration
* of the key in case the prompt or pane is destroyed by the callback.
*/
enum prompt_key_result
window_pane_prompt_key(struct window_pane *wp, struct client *c, key_code key,
struct mouse_event *m)
{
struct prompt *prompt = wp->prompt;
struct window_pane_prompt *wpp = wp->prompt_data;
enum prompt_key_result result;
u_int wp_id = wp->id, x, y, py;
int redraw = 0;
if (prompt == NULL)
return (PROMPT_KEY_NOT_HANDLED);
if (wpp != NULL)
wpp->c = c;
if (KEYC_IS_MOUSE(key)) {
if (m == NULL ||
MOUSE_BUTTONS(m->b) != MOUSE_BUTTON_1 ||
MOUSE_DRAG(m->b) ||
MOUSE_RELEASE(m->b) ||
cmd_mouse_at(wp, m, &x, &y, 0) != 0)
result = PROMPT_KEY_NOT_HANDLED;
else {
if (c != NULL && status_at_line(c) == 0)
py = 0;
else
py = wp->sy - 1;
if (y == py) {
result = prompt_mouse(prompt, x, 0, wp->sx,
&redraw);
} else
result = PROMPT_KEY_NOT_HANDLED;
}
} else
result = prompt_key(prompt, key, &redraw);
wp = window_pane_find_by_id(wp_id);
if (wp == NULL)
return (result);
if (wpp != NULL && wp->prompt_data == wpp)
wpp->c = NULL;
/*
* Only an explicit close or the prompt marking itself closed ends it;
* cursor movement and editing keep it open.
*/
if (wp->prompt == prompt &&
(result == PROMPT_KEY_CLOSE || prompt_closed(prompt)))
window_pane_clear_prompt(wp);
if (redraw || wp->prompt != prompt)
wp->flags |= PANE_REDRAW;
return (result);
}
static void
window_pane_copy_paste(struct window_pane *wp, char *buf, size_t len)
{
@@ -1450,6 +1687,12 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
wme = TAILQ_FIRST(&wp->modes);
if (wme != NULL) {
/*
* No mode uses mouse motion events, so drop them here rather
* than passing them on and causing a redraw on every movement.
*/
if (KEYC_IS_TYPE(key, KEYC_TYPE_MOUSEMOVE))
return (0);
if (wme->mode->key != NULL && c != NULL) {
key &= ~KEYC_MASK_FLAGS;
wme->mode->key(wme, c, s, wl, key, m);
@@ -1563,17 +1806,13 @@ window_pane_full_size_offset(struct window_pane *wp, int *xoff, int *yoff,
u_int *sx, u_int *sy)
{
struct window *w = wp->window;
int pane_scrollbars;
u_int sb_w, sb_pos;
u_int sb_w;
pane_scrollbars = options_get_number(w->options, "pane-scrollbars");
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
if (window_pane_show_scrollbar(wp, pane_scrollbars))
if (window_pane_scrollbar_reserve(wp))
sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
else
sb_w = 0;
if (sb_pos == PANE_SCROLLBARS_LEFT) {
if (w->sb_pos == PANE_SCROLLBARS_LEFT) {
*xoff = wp->xoff - sb_w;
*sx = wp->sx + sb_w;
} else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
@@ -1989,19 +2228,93 @@ window_pane_mode(struct window_pane *wp)
return (WINDOW_PANE_NO_MODE);
}
/* Return 1 if scrollbar is or should be displayed. */
int
window_pane_show_scrollbar(struct window_pane *wp, int sb_option)
window_pane_show_scrollbar(struct window_pane *wp)
{
if (SCREEN_IS_ALTERNATE(&wp->base))
return (0);
if (sb_option == PANE_SCROLLBARS_ALWAYS ||
(sb_option == PANE_SCROLLBARS_MODAL &&
if (wp->window->sb == PANE_SCROLLBARS_ALWAYS ||
wp->window->sb == PANE_SCROLLBARS_AUTOHIDE ||
(wp->window->sb == PANE_SCROLLBARS_MODAL &&
window_pane_mode(wp) != WINDOW_PANE_NO_MODE))
return (1);
return (0);
}
int
window_pane_scrollbar_reserve(struct window_pane *wp)
{
if (!window_pane_show_scrollbar(wp))
return (0);
return (wp->window->sb == PANE_SCROLLBARS_ALWAYS);
}
int
window_pane_scrollbar_overlay(struct window_pane *wp)
{
if (!window_pane_show_scrollbar(wp))
return (0);
return (window_pane_scrollbar_auto_hide(wp));
}
int
window_pane_scrollbar_visible(struct window_pane *wp)
{
if (!window_pane_show_scrollbar(wp))
return (0);
if (!window_pane_scrollbar_auto_hide(wp))
return (1);
return (wp->sb_auto_visible);
}
void
window_pane_scrollbar_start_timer(struct window_pane *wp)
{
struct timeval tv;
u_int delay;
if (!window_pane_scrollbar_auto_hide(wp) || !wp->sb_auto_visible)
return;
delay = options_get_number(wp->window->options,
"pane-scrollbars-timeout");
tv.tv_sec = delay / 1000;
tv.tv_usec = (delay % 1000) * 1000L;
evtimer_del(&wp->sb_auto_timer);
evtimer_add(&wp->sb_auto_timer, &tv);
}
void
window_pane_scrollbar_show(struct window_pane *wp, int start_timer)
{
int changed = 0;
if (!window_pane_scrollbar_auto_hide(wp))
return;
if (!window_pane_show_scrollbar(wp))
return;
if (!wp->sb_auto_visible) {
wp->sb_auto_visible = 1;
changed = 1;
}
evtimer_del(&wp->sb_auto_timer);
if (start_timer)
window_pane_scrollbar_start_timer(wp);
if (changed)
window_pane_scrollbar_redraw_visibility(wp);
}
void
window_pane_scrollbar_hide(struct window_pane *wp)
{
if (event_initialized(&wp->sb_auto_timer))
evtimer_del(&wp->sb_auto_timer);
wp->sb_auto_hover = 0;
if (!wp->sb_auto_visible)
return;
wp->sb_auto_visible = 0;
window_pane_scrollbar_redraw_visibility(wp);
}
int
window_pane_get_bg(struct window_pane *wp)
{
@@ -2202,6 +2515,15 @@ window_pane_status_get_range(struct window_pane *wp, u_int x, u_int y)
return (style_ranges_get_range(srs, x - wp->xoff - 2));
}
enum pane_lines
window_get_pane_lines(struct window *w)
{
struct options *oo;
oo = w->options;
return (options_get_number(oo, "pane-border-lines"));
}
enum pane_lines
window_pane_get_pane_lines(struct window_pane *wp)
{