Compare commits

..

23 Commits
layouts ... 3.7

Author SHA1 Message Date
Nicholas Marriott
81f88f8517 Add a missing {. 2026-06-26 18:55:59 +01:00
Nicholas Marriott
4eafb27b89 tmux 3.7. 2026-06-26 18:50:58 +01:00
Nicholas Marriott
4fc4644652 Tweak. 2026-06-26 18:50:42 +01:00
nicm
e7f414416f Set check callback for menus so they aren't overwritten by sync. 2026-06-23 08:35:09 +01:00
nicm
3b8e25fef4 Initialize wname so it we are not freeing garbage if it is not used. 2026-06-17 09:11:00 +01:00
nicm
64e83caf04 Be more strict about what names and titles we allow and reject them
immediately when possible, but allow # again for those directly set by
commands (but not escape sequences). From Barrett Ruth in GitHub issue
5175.
2026-06-17 09:10:55 +01:00
nicm
762e503978 Allow rectangle selection to extend past end of current line to behave
the same as vi with virtualedit=block set. From Mark Kelly in GitHub
issue 5227.
2026-06-16 11:11:47 +01:00
Nicholas Marriott
c809aea278 Fix a merge problem. 2026-06-16 10:24:06 +01:00
Nicholas Marriott
1596f9c9f5 Bump version again. 2026-06-16 10:22:51 +01:00
nicm
2f28d9a168 Fix resizing floating pane with a left scrollbar, from Michael Grant. 2026-06-16 10:22:12 +01:00
Nicholas Marriott
baac29debc Prefer libtinfow if it is available, GitHub issue 5224 from Lars Wendler. 2026-06-16 10:21:12 +01:00
nicm
2c4df765c5 Fix missing border when drawing floating panes. From Michael Grant. 2026-06-16 10:21:09 +01:00
nicm
2d9afa6348 Use correct x position when drawing clipped line. 2026-06-15 18:39:43 +01:00
nicm
bae5c14941 Tidy up screen_redraw_get_visible_ranges by using a couple of
temporaries for start and end of range.
2026-06-15 18:39:28 +01:00
nicm
9cf8de099c Convert cursor position back to pane coordinates for tty_cmd_cell. 2026-06-15 18:39:23 +01:00
nicm
f98aaf544d When redrawing a whole pane line, fall into tty_draw_line for any
characters that are not plain ASCII. Fixes redraw issues with partial
tabs and other wide characters when a pane is partly outside the window.
2026-06-15 09:07:23 +01:00
nicm
78afc24736 Fix various errors in redrawing:
- Fix the active pane colour when only two panes and scrollbars enabled.

- Clip left and right scrollbars the same for floating panes.

- Do not subtract scrollbar width twice when working out width of status
  line.

- Check if a character is inside a visible range correctly (do not
  include the next position outside the range).
2026-06-15 09:06:58 +01:00
nicm
83e4534387 Skip floating panes when working out the top or bottom cell. Fixes
missing bottom status pane status line when floating panes exist.
2026-06-15 09:06:55 +01:00
nicm
dabaae1534 Make buffer creation time sort oldest first like it used to, but change
windows to match sessions and panes as newest first.
2026-06-12 10:19:05 +01:00
nicm
cc87db74c7 Two fixes for RI codepoints. Firstly, do not combine more than two of
them - previously we were ending up with four codepoints in one cell
which tmux believed to be width 2, but terminals considered width 4.
Secondly, invalidate cursor position before redrawing the cell when the
second codepoint is received, terminals vary in how they manage
backspace and cursor movement across these characters, so it is better
to use absolute rather than relative positioning. GitHub issue 4853.
2026-06-09 13:20:14 +01:00
Nicholas Marriott
aa1f0653e9 Bump version to 3.7-rc2. 2026-06-09 09:47:18 +01:00
nicm
8771b6051f Fix mouse events on tiled pane status line - when panes share a border,
prefer the pane for which the border is the status line. With Dane
Jensen.
2026-06-09 09:12:21 +01:00
Nicholas Marriott
bbd4768bb6 3.7-rc version. 2026-06-08 21:08:37 +01:00
196 changed files with 4721 additions and 12604 deletions

View File

@@ -63,7 +63,7 @@ tmux -vv -Ltest -f/dev/null new
Or if you need your configuration: Or if you need your configuration:
~~~bash ~~~base
tmux kill-server tmux kill-server
tmux -vv new tmux -vv new
~~~ ~~~

2
.gitignore vendored
View File

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

View File

@@ -5,6 +5,7 @@ CHANGES FROM 3.6b TO 3.7
the same escape sequence support). Floating panes are created with the the same escape sequence support). Floating panes are created with the
new-pane command, bound to * by default. new-pane command, bound to * by default.
This is an early release of this feature and they are relatively limited.
Currently floating panes can only be moved and resized using the mouse. The Currently floating panes can only be moved and resized using the mouse. The
default second status line (if status-format is set to 2) has changed to show default second status line (if status-format is set to 2) has changed to show
a list of panes. Many obvious features are not yet available for floating a list of panes. Many obvious features are not yet available for floating

View File

@@ -184,8 +184,6 @@ dist_tmux_SOURCES = \
paste.c \ paste.c \
popup.c \ popup.c \
proc.c \ proc.c \
prompt.c \
prompt-history.c \
regsub.c \ regsub.c \
resize.c \ resize.c \
screen-redraw.c \ screen-redraw.c \
@@ -211,14 +209,12 @@ dist_tmux_SOURCES = \
tty.c \ tty.c \
utf8-combined.c \ utf8-combined.c \
utf8.c \ utf8.c \
window-border.c \
window-buffer.c \ window-buffer.c \
window-client.c \ window-client.c \
window-clock.c \ window-clock.c \
window-copy.c \ window-copy.c \
window-customize.c \ window-customize.c \
window-tree.c \ window-tree.c \
window-visible.c \
window.c \ window.c \
xmalloc.c \ xmalloc.c \
xmalloc.h xmalloc.h

View File

@@ -998,7 +998,7 @@ args_string_percentage(const char *value, long long minval, long long maxval,
copy = xstrdup(value); copy = xstrdup(value);
copy[valuelen - 1] = '\0'; copy[valuelen - 1] = '\0';
ll = strtonum(copy, 0, 1000, &errstr); ll = strtonum(copy, 0, 100, &errstr);
free(copy); free(copy);
if (errstr != NULL) { if (errstr != NULL) {
*cause = xstrdup(errstr); *cause = xstrdup(errstr);
@@ -1066,7 +1066,7 @@ args_string_percentage_and_expand(const char *value, long long minval,
copy[valuelen - 1] = '\0'; copy[valuelen - 1] = '\0';
f = format_single_from_target(item, copy); f = format_single_from_target(item, copy);
ll = strtonum(f, 0, 1000, &errstr); ll = strtonum(f, 0, 100, &errstr);
free(f); free(f);
free(copy); free(copy);
if (errstr != NULL) { 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) if (cfg_item != NULL)
cmdq_continue(cfg_item); cmdq_continue(cfg_item);
prompt_load_history(); status_prompt_load_history();
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -114,7 +114,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
w->active = wp; w->active = wp;
w->latest = tc; w->latest = tc;
if (name == NULL) { if (name != NULL) {
newname = default_window_name(w); newname = default_window_name(w);
window_set_name(w, newname, WINDOW_NAME_FORBID); window_set_name(w, newname, WINDOW_NAME_FORBID);
free(newname); free(newname);

View File

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

View File

@@ -33,8 +33,8 @@ const struct cmd_entry cmd_choose_tree_entry = {
.name = "choose-tree", .name = "choose-tree",
.alias = NULL, .alias = NULL,
.args = { "F:f:GhK:kNO:rst:wyZ", 0, 1, cmd_choose_tree_args_parse }, .args = { "F:f:GK:NO:rst:wyZ", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-GhkNrswZ] [-F format] [-f filter] [-K key-format] " .usage = "[-GNrswZ] [-F format] [-f filter] [-K key-format] "
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -47,8 +47,8 @@ const struct cmd_entry cmd_choose_client_entry = {
.name = "choose-client", .name = "choose-client",
.alias = NULL, .alias = NULL,
.args = { "F:f:hiK:kNO:rt:yZ", 0, 1, cmd_choose_tree_args_parse }, .args = { "F:f:K:NO:rt:yZ", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-hikNrZ] [-F format] [-f filter] [-K key-format] " .usage = "[-NrZ] [-F format] [-f filter] [-K key-format] "
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -61,8 +61,8 @@ const struct cmd_entry cmd_choose_buffer_entry = {
.name = "choose-buffer", .name = "choose-buffer",
.alias = NULL, .alias = NULL,
.args = { "F:f:K:kNO:rt:yZ", 0, 1, cmd_choose_tree_args_parse }, .args = { "F:f:K:NO:rt:yZ", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-kNrZ] [-F format] [-f filter] [-K key-format] " .usage = "[-NrZ] [-F format] [-f filter] [-K key-format] "
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -75,8 +75,8 @@ const struct cmd_entry cmd_customize_mode_entry = {
.name = "customize-mode", .name = "customize-mode",
.alias = NULL, .alias = NULL,
.args = { "F:f:kNt:yZ", 0, 0, NULL }, .args = { "F:f:Nt:yZ", 0, 0, NULL },
.usage = "[-kNZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE, .usage = "[-NZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },

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

View File

@@ -48,16 +48,6 @@ struct cmd_display_panes_data {
struct args_command_state *state; 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 static enum args_parse_type
cmd_display_panes_args_parse(__unused struct args *args, __unused u_int idx, cmd_display_panes_args_parse(__unused struct args *args, __unused u_int idx,
__unused char **cause) __unused char **cause)
@@ -66,7 +56,7 @@ cmd_display_panes_args_parse(__unused struct args *args, __unused u_int idx,
} }
static void static void
cmd_display_panes_put(struct cmd_display_panes_ctx *ctx, cmd_display_panes_put(struct screen_redraw_ctx *ctx,
struct window_pane *wp, u_int cx, u_int cy, const char *buf, size_t len) struct window_pane *wp, u_int cx, u_int cy, const char *buf, size_t len)
{ {
struct client *c = ctx->c; struct client *c = ctx->c;
@@ -75,7 +65,8 @@ cmd_display_panes_put(struct cmd_display_panes_ctx *ctx,
struct visible_range *ri; struct visible_range *ri;
u_int i, j; u_int i, j;
r = window_visible_ranges(wp, ctx->ox + cx, ctx->oy + cy, len, NULL); r = screen_redraw_get_visible_ranges(wp, ctx->ox + cx, ctx->oy + cy,
len, NULL);
for (i = 0; i < r->used; i++) { for (i = 0; i < r->used; i++) {
ri = &r->ranges[i]; ri = &r->ranges[i];
for (j = ri->px; j < ri->px + ri->nx; j++) { for (j = ri->px; j < ri->px + ri->nx; j++) {
@@ -86,43 +77,7 @@ cmd_display_panes_put(struct cmd_display_panes_ctx *ctx,
} }
static void static void
cmd_display_panes_draw_format(struct cmd_display_panes_ctx *ctx, cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
struct window_pane *wp, u_int xoff, u_int yoff, u_int sx,
const struct grid_cell *gc)
{
struct client *c = ctx->c;
struct tty *tty = &c->tty;
struct session *s = c->session;
struct screen *sc = wp->screen, screen;
struct screen_write_ctx sctx;
struct visible_ranges *r;
struct visible_range *ri;
const char *format;
char *expanded;
u_int i, px = ctx->ox + xoff;
format = options_get_string(s->options, "display-panes-format");
expanded = format_single(NULL, format, c, s, s->curw, wp);
screen_init(&screen, sx, 1, 0);
screen_write_start(&sctx, &screen);
screen_write_fast_copy(&sctx, sc, 0, sc->grid->hsize, sx, 1);
screen_write_cursormove(&sctx, 0, 0, 0);
format_draw(&sctx, gc, sx, expanded, NULL, 0);
screen_write_stop(&sctx);
free(expanded);
r = window_visible_ranges(wp, px, wp->yoff, sx, NULL);
for (i = 0; i < r->used; i++) {
ri = &r->ranges[i];
tty_draw_line(tty, &screen, ri->px - px, 0, ri->nx,
ri->px - ctx->ox, yoff, NULL);
}
screen_free(&screen);
}
static void
cmd_display_panes_draw_pane(struct cmd_display_panes_ctx *ctx,
struct window_pane *wp) struct window_pane *wp)
{ {
struct client *c = ctx->c; struct client *c = ctx->c;
@@ -133,10 +88,9 @@ cmd_display_panes_draw_pane(struct cmd_display_panes_ctx *ctx,
struct grid_cell fgc, bgc; struct grid_cell fgc, bgc;
u_int pane, idx, px, py, i, j, xoff, yoff, sx, sy; u_int pane, idx, px, py, i, j, xoff, yoff, sx, sy;
u_int cx, cy; u_int cx, cy;
const char *name; int colour, active_colour;
struct format_tree *ft; char buf[16], lbuf[16], rbuf[16], *ptr;
char buf[16], lbuf[16], *ptr; size_t len, llen, rlen;
size_t len, llen;
if (wp->xoff + (int)wp->sx <= ctx->ox || if (wp->xoff + (int)wp->sx <= ctx->ox ||
wp->xoff >= ctx->ox + (int)ctx->sx || wp->xoff >= ctx->ox + (int)ctx->sx ||
@@ -192,24 +146,27 @@ cmd_display_panes_draw_pane(struct cmd_display_panes_ctx *ctx,
if (sx < len) if (sx < len)
return; return;
if (w->active == wp) colour = options_get_number(oo, "display-panes-colour");
name = "display-panes-active-colour"; active_colour = options_get_number(oo, "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); memcpy(&bgc, &grid_default_cell, sizeof bgc);
bgc.bg = fgc.fg; if (w->active == wp) {
fgc.fg = active_colour;
bgc.bg = active_colour;
} else {
fgc.fg = colour;
bgc.bg = colour;
}
rlen = xsnprintf(rbuf, sizeof rbuf, "%ux%u", wp->sx, wp->sy);
if (pane > 9 && pane < 35) if (pane > 9 && pane < 35)
llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10)); llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10));
else else
llen = 0; llen = 0;
if (sx < len * 6 || sy < 5) { if (sx < len * 6 || sy < 5) {
tty_attributes(tty, &fgc, NULL); tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
if (sx >= len + llen + 1) { if (sx >= len + llen + 1) {
len += llen + 1; len += llen + 1;
cx = xoff + px - len / 2; cx = xoff + px - len / 2;
@@ -230,7 +187,7 @@ cmd_display_panes_draw_pane(struct cmd_display_panes_ctx *ctx,
px -= len * 3; px -= len * 3;
py -= 2; py -= 2;
tty_attributes(tty, &bgc, NULL); tty_attributes(tty, &bgc, &grid_default_cell, NULL, NULL);
for (ptr = buf; *ptr != '\0'; ptr++) { for (ptr = buf; *ptr != '\0'; ptr++) {
if (*ptr < '0' || *ptr > '9') if (*ptr < '0' || *ptr > '9')
continue; continue;
@@ -250,9 +207,13 @@ cmd_display_panes_draw_pane(struct cmd_display_panes_ctx *ctx,
if (sy <= 6) if (sy <= 6)
goto out; goto out;
cmd_display_panes_draw_format(ctx, wp, xoff, yoff, sx, &fgc); tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
if (rlen != 0 && sx >= rlen) {
cx = xoff + sx - rlen;
cy = yoff;
cmd_display_panes_put(ctx, wp, cx, cy, rbuf, rlen);
}
if (llen != 0) { if (llen != 0) {
tty_attributes(tty, &fgc, NULL);
cx = xoff + sx / 2 + len * 3 - llen - 1; cx = xoff + sx / 2 + len * 3 - llen - 1;
cy = yoff + py + 5; cy = yoff + py + 5;
cmd_display_panes_put(ctx, wp, cx, cy, lbuf, llen); cmd_display_panes_put(ctx, wp, cx, cy, lbuf, llen);
@@ -263,30 +224,17 @@ out:
} }
static void static void
cmd_display_panes_draw(struct client *c, __unused void *data) cmd_display_panes_draw(struct client *c, __unused void *data,
struct screen_redraw_ctx *ctx)
{ {
struct session *s = c->session; struct window *w = c->session->curw->window;
struct window *w = s->curw->window; struct window_pane *wp;
struct window_pane *wp;
struct cmd_display_panes_ctx ctx;
u_int lines;
log_debug("%s: %s @%u", __func__, c->name, w->id); 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) { TAILQ_FOREACH(wp, &w->panes, entry) {
if (window_pane_is_visible(wp)) if (window_pane_visible(wp))
cmd_display_panes_draw_pane(&ctx, wp); cmd_display_panes_draw_pane(ctx, wp);
} }
} }

View File

@@ -147,18 +147,6 @@ cmd_find_session_better(struct session *s, struct session *than, int flags)
return (timercmp(&s->activity_time, &than->activity_time, >)); return (timercmp(&s->activity_time, &than->activity_time, >));
} }
/* Can this session be usefully targeted? */
static int
cmd_find_session_valid(struct session *s)
{
if (!session_alive(s) ||
s->curw == NULL ||
s->curw->window == NULL ||
s->curw->window->active == NULL)
return (0);
return (1);
}
/* Find best session from a list, or all if list is NULL. */ /* Find best session from a list, or all if list is NULL. */
static struct session * static struct session *
cmd_find_best_session(struct session **slist, u_int ssize, int flags) cmd_find_best_session(struct session **slist, u_int ssize, int flags)
@@ -171,15 +159,11 @@ cmd_find_best_session(struct session **slist, u_int ssize, int flags)
s = NULL; s = NULL;
if (slist != NULL) { if (slist != NULL) {
for (i = 0; i < ssize; i++) { for (i = 0; i < ssize; i++) {
if (!cmd_find_session_valid(slist[i]))
continue;
if (cmd_find_session_better(slist[i], s, flags)) if (cmd_find_session_better(slist[i], s, flags))
s = slist[i]; s = slist[i];
} }
} else { } else {
RB_FOREACH(s_loop, sessions, &sessions) { RB_FOREACH(s_loop, sessions, &sessions) {
if (!cmd_find_session_valid(s_loop))
continue;
if (cmd_find_session_better(s_loop, s, flags)) if (cmd_find_session_better(s_loop, s, flags))
s = s_loop; s = s_loop;
} }

View File

@@ -30,9 +30,6 @@
*/ */
static enum cmd_retval cmd_join_pane_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_join_pane_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_join_pane_mouse_update(struct cmdq_item *);
static void cmd_join_pane_mouse_move(struct client *,
struct mouse_event *);
const struct cmd_entry cmd_join_pane_entry = { const struct cmd_entry cmd_join_pane_entry = {
.name = "join-pane", .name = "join-pane",
@@ -52,10 +49,8 @@ const struct cmd_entry cmd_move_pane_entry = {
.name = "move-pane", .name = "move-pane",
.alias = "movep", .alias = "movep",
.args = { "bdfhMvl:L::P:R::s:t:U::X:Y:z:", 0, 0, NULL }, .args = { "bdfhvp:l:s:t:", 0, 0, NULL },
.usage = "[-bdfhMv] [-D lines] [-l size] [-L columns] [-P position] " .usage = "[-bdfhv] [-l size] " CMD_SRCDST_PANE_USAGE,
"[-R columns] " CMD_SRCDST_PANE_USAGE " [-U lines] "
"[-X x-position] [-Y y-position] [-z z-index]",
.source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED }, .source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED },
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -64,305 +59,6 @@ const struct cmd_entry cmd_move_pane_entry = {
.exec = cmd_join_pane_exec .exec = cmd_join_pane_exec
}; };
static enum cmd_retval
cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
struct window_pane *wp, const char *position)
{
struct window *w = wl->window;
struct layout_cell *lc = wp->layout_cell;
struct window_pane *owp;
int wx = w->sx, wy = w->sy, px = lc->sx;
int py = lc->sy, xoff = lc->xoff, yoff = lc->yoff;
int border = 1;
if (window_pane_get_pane_lines(wp) == PANE_LINES_NONE)
border = 0;
if (strcmp(position, "top-left") == 0) {
xoff = border;
yoff = border;
} else if (strcmp(position, "top-centre") == 0 ||
strcmp(position, "top-center") == 0) {
xoff = (wx - px) / 2;
yoff = border;
} else if (strcmp(position, "top-right") == 0) {
xoff = wx - px - border;
yoff = border;
} else if (strcmp(position, "centre-left") == 0 ||
strcmp(position, "center-left") == 0) {
xoff = border;
yoff = (wy - py) / 2;
} else if (strcmp(position, "centre") == 0 ||
strcmp(position, "center") == 0) {
xoff = (wx - px) / 2;
yoff = (wy - py) / 2;
} else if (strcmp(position, "centre-right") == 0 ||
strcmp(position, "center-right") == 0) {
xoff = wx - px - border;
yoff = (wy - py) / 2;
} else if (strcmp(position, "bottom-left") == 0) {
xoff = border;
yoff = wy - py - border;
} else if (strcmp(position, "bottom-centre") == 0 ||
strcmp(position, "bottom-center") == 0) {
xoff = (wx - px) / 2;
yoff = wy - py - border;
} else if (strcmp(position, "bottom-right") == 0) {
xoff = wx - px - border;
yoff = wy - py - border;
} else if (strcmp(position, "top-left-centre") == 0 ||
strcmp(position, "top-left-center") == 0) {
xoff = wx / 4 - px / 2;
yoff = wy / 4 - py / 2;
} else if (strcmp(position, "top-right-centre") == 0 ||
strcmp(position, "top-right-center") == 0) {
xoff = (3 * wx) / 4 - px / 2;
yoff = wy / 4 - py / 2;
} else if (strcmp(position, "bottom-left-centre") == 0 ||
strcmp(position, "bottom-left-center") == 0) {
xoff = wx / 4 - px / 2;
yoff = (3 * wy) / 4 - py / 2;
} else if (strcmp(position, "bottom-right-centre") == 0 ||
strcmp(position, "bottom-right-center") == 0) {
xoff = (3 * wx) / 4 - px / 2;
yoff = (3 * wy) / 4 - py / 2;
} else if (strcmp(position, "front") == 0) {
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
} else if (strcmp(position, "back") == 0) {
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_FOREACH(owp, &w->z_index, zentry) {
if (!window_pane_is_floating(owp))
break;
}
if (owp != NULL)
TAILQ_INSERT_BEFORE(owp, wp, zentry);
else
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
} else if (strcmp(position, "forward") == 0) {
owp = TAILQ_PREV(wp, window_panes_zindex, zentry);
if (owp != NULL) {
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_BEFORE(owp, wp, zentry);
}
} else if (strcmp(position, "backward") == 0) {
owp = TAILQ_NEXT(wp, zentry);
if (owp != NULL && window_pane_is_floating(owp)) {
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry);
}
} else if (strcmp(position, "forward-loop") == 0) {
owp = TAILQ_PREV(wp, window_panes_zindex, zentry);
TAILQ_REMOVE(&w->z_index, wp, zentry);
if (owp != NULL)
TAILQ_INSERT_BEFORE(owp, wp, zentry);
else {
TAILQ_FOREACH(owp, &w->z_index, zentry) {
if (!window_pane_is_floating(owp))
break;
}
if (owp != NULL)
TAILQ_INSERT_BEFORE(owp, wp, zentry);
else
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
}
} else if (strcmp(position, "backward-loop") == 0) {
owp = TAILQ_NEXT(wp, zentry);
if (owp != NULL && window_pane_is_floating(owp)) {
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry);
} else {
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
}
} else {
cmdq_error(item, "unknown position: %s", position);
return (CMD_RETURN_ERROR);
}
if (xoff != lc->xoff || yoff != lc->yoff) {
lc->xoff = xoff;
lc->yoff = yoff;
layout_fix_panes(w, NULL);
}
notify_window("window-layout-changed", w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_join_pane_move(struct cmdq_item *item, struct args *args,
struct winlink *wl, struct window_pane *wp)
{
struct window *w = wl->window;
struct layout_cell *lc = wp->layout_cell;
const char *errstr, *argval;
const char flags[] = { 'U', 'D', 'L', 'R' };
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)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)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++) {
flag = flags[i];
if (!args_has(args, flag))
continue;
argval = args_get(args, flag);
if (argval == NULL)
argval = "1";
adjust = strtonum(argval, INT_MIN, INT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "offset %s", errstr);
return (CMD_RETURN_ERROR);
}
if (flag == 'U')
yoff -= adjust;
else if (flag == 'D')
yoff += adjust;
else if (flag == 'L')
xoff -= adjust;
else
xoff += adjust;
}
if (xoff != lc->xoff || yoff != lc->yoff) {
lc->xoff = xoff;
lc->yoff = yoff;
layout_fix_panes(w, NULL);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_join_pane_mouse_update(struct cmdq_item *item)
{
struct cmd_find_state *target = cmdq_get_target(item);
struct key_event *event = cmdq_get_event(item);
struct client *c = cmdq_get_client(item);
struct session *s = target->s;
struct winlink *wl;
struct window *w;
struct window_pane *wp;
if (!event->m.valid)
return (CMD_RETURN_NORMAL);
wp = cmd_mouse_pane(&event->m, &s, &wl);
if (wp == NULL || c == NULL || c->session != s)
return (CMD_RETURN_NORMAL);
if (!window_pane_is_floating(wp))
return (CMD_RETURN_NORMAL);
w = wl->window;
window_redraw_active_switch(w, wp);
window_set_active_pane(w, wp, 1);
c->tty.mouse_drag_update = cmd_join_pane_mouse_move;
cmd_join_pane_mouse_move(c, &event->m);
return (CMD_RETURN_NORMAL);
}
static void
cmd_join_pane_mouse_move(struct client *c, struct mouse_event *m)
{
struct winlink *wl;
struct window *w;
struct window_pane *wp;
struct layout_cell *lc;
int y, ly, x, lx;
wp = cmd_mouse_pane(m, NULL, &wl);
if (wp == NULL) {
c->tty.mouse_drag_update = NULL;
return;
}
w = wl->window;
lc = wp->layout_cell;
y = m->y + m->oy; x = m->x + m->ox;
if (m->statusat == 0 && y >= (int)m->statuslines)
y -= m->statuslines;
else if (m->statusat > 0 && y >= m->statusat)
y = m->statusat - 1;
ly = m->ly + m->oy; lx = m->lx + m->ox;
if (m->statusat == 0 && ly >= (int)m->statuslines)
ly -= m->statuslines;
else if (m->statusat > 0 && ly >= m->statusat)
ly = m->statusat - 1;
if (x != lx || y != ly) {
lc->xoff += x - lx;
lc->yoff += y - ly;
layout_fix_panes(w, NULL);
server_redraw_window(w);
server_redraw_window_borders(w);
}
}
static enum cmd_retval
cmd_join_pane_zindex(struct cmdq_item *item, struct winlink *wl,
struct window_pane *wp, const char *s)
{
struct window *w = wl->window;
struct window_pane *owp;
const char *errstr;
u_int n, z;
z = strtonum(s, 0, UINT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "z-index %s", errstr);
return (CMD_RETURN_ERROR);
}
TAILQ_REMOVE(&w->z_index, wp, zentry);
n = 0;
TAILQ_FOREACH(owp, &w->z_index, zentry) {
if (!window_pane_is_floating(owp))
break;
if (n >= z)
break;
n++;
}
if (owp != NULL)
TAILQ_INSERT_BEFORE(owp, wp, zentry);
else
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
notify_window("window-layout-changed", w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval static enum cmd_retval
cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
{ {
@@ -374,7 +70,6 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
struct winlink *src_wl, *dst_wl; struct winlink *src_wl, *dst_wl;
struct window *src_w, *dst_w; struct window *src_w, *dst_w;
struct window_pane *src_wp, *dst_wp; struct window_pane *src_wp, *dst_wp;
const char *s;
char *cause = NULL; char *cause = NULL;
int flags = 0, dst_idx; int flags = 0, dst_idx;
struct layout_cell *lc; struct layout_cell *lc;
@@ -386,26 +81,6 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
dst_idx = dst_wl->idx; dst_idx = dst_wl->idx;
server_unzoom_window(dst_w); server_unzoom_window(dst_w);
if (cmd_get_entry(self) == &cmd_move_pane_entry) {
if (args_has(args, 'M'))
return (cmd_join_pane_mouse_update(item));
if (!window_pane_is_floating(dst_wp)) {
cmdq_error(item, "pane is not floating");
return (CMD_RETURN_ERROR);
}
if ((s = args_get(args, 'P')) != NULL)
return (cmd_join_pane_place(item, dst_wl, dst_wp, s));
if ((s = args_get(args, 'z')) != NULL)
return (cmd_join_pane_zindex(item, dst_wl, dst_wp, s));
if (args_has(args, 'X') ||
args_has(args, 'Y') ||
args_has(args, 'U') ||
args_has(args, 'D') ||
args_has(args, 'L') ||
args_has(args, 'R'))
return (cmd_join_pane_move(item, args, dst_wl, dst_wp));
}
src_wl = source->wl; src_wl = source->wl;
src_wp = source->wp; src_wp = source->wp;
src_w = src_wl->window; src_w = src_wl->window;

View File

@@ -27,17 +27,13 @@
*/ */
static enum cmd_retval cmd_kill_pane_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_kill_pane_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_kill_pane_all(struct cmdq_item *, const char *);
static int cmd_kill_pane_filter(struct cmdq_item *,
struct session *, struct winlink *,
struct window_pane *, const char *);
const struct cmd_entry cmd_kill_pane_entry = { const struct cmd_entry cmd_kill_pane_entry = {
.name = "kill-pane", .name = "kill-pane",
.alias = "killp", .alias = "killp",
.args = { "af:t:", 0, 0, NULL }, .args = { "at:", 0, 0, NULL },
.usage = "[-a] [-f filter] " CMD_TARGET_PANE_USAGE, .usage = "[-a] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -50,17 +46,22 @@ cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct window_pane *wp = target->wp; struct winlink *wl = target->wl;
const char *filter = args_get(args, 'f'); struct window_pane *loopwp, *tmpwp, *wp = target->wp;
if (filter != NULL && !args_has(args, 'a')) { if (args_has(args, 'a')) {
cmdq_error(item, "-f only valid with -a"); server_unzoom_window(wl->window);
return (CMD_RETURN_ERROR); TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) {
if (loopwp == wp)
continue;
server_client_remove_pane(loopwp);
layout_close_pane(loopwp);
window_remove_pane(wl->window, loopwp);
}
server_redraw_window(wl->window);
return (CMD_RETURN_NORMAL);
} }
if (args_has(args, 'a'))
return (cmd_kill_pane_all(item, filter));
if (wp == NULL) { if (wp == NULL) {
cmdq_error(item, "no active pane to kill"); cmdq_error(item, "no active pane to kill");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
@@ -68,48 +69,3 @@ cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item)
server_kill_pane(wp); server_kill_pane(wp);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
static enum cmd_retval
cmd_kill_pane_all(struct cmdq_item *item, const char *filter)
{
struct cmd_find_state *target = cmdq_get_target(item);
struct session *s = target->s;
struct winlink *wl = target->wl;
struct window_pane *wp = target->wp;
struct window_pane *loopwp, *tmpwp;
server_unzoom_window(wl->window);
TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) {
if (loopwp == wp)
continue;
if (!cmd_kill_pane_filter(item, s, wl, loopwp, filter))
continue;
server_client_remove_pane(loopwp);
layout_close_pane(loopwp);
window_remove_pane(wl->window, loopwp);
}
server_redraw_window(wl->window);
return (CMD_RETURN_NORMAL);
}
static int
cmd_kill_pane_filter(struct cmdq_item *item, struct session *s,
struct winlink *wl, struct window_pane *wp, const char *filter)
{
struct format_tree *ft;
char *expanded;
int flag;
if (filter == NULL)
return (1);
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, s, wl, wp);
expanded = format_expand(ft, filter);
flag = format_true(expanded);
free(expanded);
format_free(ft);
return (flag);
}

View File

@@ -18,8 +18,6 @@
#include <sys/types.h> #include <sys/types.h>
#include <stdlib.h>
#include "tmux.h" #include "tmux.h"
/* /*
@@ -30,16 +28,13 @@
*/ */
static enum cmd_retval cmd_kill_session_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_kill_session_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_kill_session_all(struct cmdq_item *, const char *);
static int cmd_kill_session_filter(struct cmdq_item *,
struct session *, const char *);
const struct cmd_entry cmd_kill_session_entry = { const struct cmd_entry cmd_kill_session_entry = {
.name = "kill-session", .name = "kill-session",
.alias = NULL, .alias = NULL,
.args = { "aCgf:t:", 0, 0, NULL }, .args = { "aCgt:", 0, 0, NULL },
.usage = "[-aCg] [-f filter] " CMD_TARGET_SESSION_USAGE, .usage = "[-aCg] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 }, .target = { 't', CMD_FIND_SESSION, 0 },
@@ -55,12 +50,6 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = target->s, *sloop, *stmp; struct session *s = target->s, *sloop, *stmp;
struct session_group *sg; struct session_group *sg;
struct winlink *wl; struct winlink *wl;
const char *filter = args_get(args, 'f');
if (filter != NULL && (!args_has(args, 'a') || args_has(args, 'C'))) {
cmdq_error(item, "-f only valid with -a");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'C')) { if (args_has(args, 'C')) {
RB_FOREACH(wl, winlinks, &s->windows) { RB_FOREACH(wl, winlinks, &s->windows) {
@@ -68,9 +57,14 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item)
wl->flags &= ~WINLINK_ALERTFLAGS; wl->flags &= ~WINLINK_ALERTFLAGS;
} }
server_redraw_session(s); server_redraw_session(s);
} else if (args_has(args, 'a')) } else if (args_has(args, 'a')) {
return (cmd_kill_session_all(item, filter)); RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) {
else if (args_has(args, 'g') && if (sloop != s) {
server_destroy_session(sloop);
session_destroy(sloop, 1, __func__);
}
}
} else if (args_has(args, 'g') &&
(sg = session_group_contains(s)) != NULL) { (sg = session_group_contains(s)) != NULL) {
TAILQ_FOREACH_SAFE(sloop, &sg->sessions, gentry, stmp) { TAILQ_FOREACH_SAFE(sloop, &sg->sessions, gentry, stmp) {
server_destroy_session(sloop); server_destroy_session(sloop);
@@ -82,42 +76,3 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item)
} }
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
static enum cmd_retval
cmd_kill_session_all(struct cmdq_item *item, const char *filter)
{
struct session *s = cmdq_get_target(item)->s;
struct session *sloop, *stmp;
RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) {
if (sloop == s)
continue;
if (!cmd_kill_session_filter(item, sloop, filter))
continue;
server_destroy_session(sloop);
session_destroy(sloop, 1, __func__);
}
return (CMD_RETURN_NORMAL);
}
static int
cmd_kill_session_filter(struct cmdq_item *item, struct session *s,
const char *filter)
{
struct format_tree *ft;
char *expanded;
int flag;
if (filter == NULL)
return (1);
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, s, NULL, NULL);
expanded = format_expand(ft, filter);
flag = format_true(expanded);
free(expanded);
format_free(ft);
return (flag);
}

View File

@@ -18,8 +18,6 @@
#include <sys/types.h> #include <sys/types.h>
#include <stdlib.h>
#include "tmux.h" #include "tmux.h"
/* /*
@@ -27,16 +25,13 @@
*/ */
static enum cmd_retval cmd_kill_window_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_kill_window_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_kill_window_all(struct cmdq_item *, const char *);
static int cmd_kill_window_filter(struct cmdq_item *,
struct session *, struct winlink *, const char *);
const struct cmd_entry cmd_kill_window_entry = { const struct cmd_entry cmd_kill_window_entry = {
.name = "kill-window", .name = "kill-window",
.alias = "killw", .alias = "killw",
.args = { "af:t:", 0, 0, NULL }, .args = { "at:", 0, 0, NULL },
.usage = "[-a] [-f filter] " CMD_TARGET_WINDOW_USAGE, .usage = "[-a] " CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 }, .target = { 't', CMD_FIND_WINDOW, 0 },
@@ -62,15 +57,10 @@ cmd_kill_window_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl; struct winlink *wl = target->wl, *loop;
struct window *w = wl->window; struct window *w = wl->window;
struct session *s = target->s; struct session *s = target->s;
const char *filter = args_get(args, 'f'); u_int found;
if (filter != NULL && !args_has(args, 'a')) {
cmdq_error(item, "-f only valid with -a");
return (CMD_RETURN_ERROR);
}
if (cmd_get_entry(self) == &cmd_unlink_window_entry) { if (cmd_get_entry(self) == &cmd_unlink_window_entry) {
if (!args_has(args, 'k') && !session_is_linked(s, w)) { if (!args_has(args, 'k') && !session_is_linked(s, w)) {
@@ -82,76 +72,39 @@ cmd_kill_window_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (args_has(args, 'a')) if (args_has(args, 'a')) {
return (cmd_kill_window_all(item, filter)); if (RB_PREV(winlinks, &s->windows, wl) == NULL &&
RB_NEXT(winlinks, &s->windows, wl) == NULL)
return (CMD_RETURN_NORMAL);
/* Kill all windows except the current one. */
do {
found = 0;
RB_FOREACH(loop, winlinks, &s->windows) {
if (loop->window != wl->window) {
server_kill_window(loop->window, 0);
found++;
break;
}
}
} while (found != 0);
/*
* If the current window appears in the session more than once,
* kill it as well.
*/
found = 0;
RB_FOREACH(loop, winlinks, &s->windows) {
if (loop->window == wl->window)
found++;
}
if (found > 1)
server_kill_window(wl->window, 0);
server_renumber_all();
return (CMD_RETURN_NORMAL);
}
server_kill_window(wl->window, 1); server_kill_window(wl->window, 1);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
static enum cmd_retval
cmd_kill_window_all(struct cmdq_item *item, const char *filter)
{
struct cmd_find_state *target = cmdq_get_target(item);
struct session *s = target->s;
struct winlink *wl = target->wl;
struct winlink *loop;
u_int found, kill_current;
if (RB_PREV(winlinks, &s->windows, wl) == NULL &&
RB_NEXT(winlinks, &s->windows, wl) == NULL)
return (CMD_RETURN_NORMAL);
/* Kill all windows except the current one. */
do {
found = 0;
RB_FOREACH(loop, winlinks, &s->windows) {
if (loop->window != wl->window &&
cmd_kill_window_filter(item, s, loop, filter)) {
server_kill_window(loop->window, 0);
found++;
break;
}
}
} while (found != 0);
/*
* If the current window appears in the session more than once, kill it
* as well if it matches the filter.
*/
found = kill_current = 0;
RB_FOREACH(loop, winlinks, &s->windows) {
if (loop->window == wl->window) {
found++;
if (cmd_kill_window_filter(item, s, loop, filter))
kill_current = 1;
}
}
if (kill_current && found > 1)
server_kill_window(wl->window, 0);
server_renumber_all();
return (CMD_RETURN_NORMAL);
}
static int
cmd_kill_window_filter(struct cmdq_item *item, struct session *s,
struct winlink *wl, const char *filter)
{
struct format_tree *ft;
char *expanded;
int flag;
if (filter == NULL)
return (1);
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, s, wl, NULL);
expanded = format_expand(ft, filter);
flag = format_true(expanded);
free(expanded);
format_free(ft);
return (flag);
}

View File

@@ -223,12 +223,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
cmd_list_keys_filter_key_list(filter_notes, filter_key, only, l, cmd_list_keys_filter_key_list(filter_notes, filter_key, only, l,
&n); &n);
} }
if (filter_key && n == 0) { if (single)
cmdq_error(item, "unknown key: %s", keystr);
free(prefix);
return (CMD_RETURN_ERROR);
}
if (single && n > 1)
n = 1; n = 1;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
@@ -242,7 +237,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
cmd_list_keys_format_add_key_binding(ft, l[i], prefix); cmd_list_keys_format_add_key_binding(ft, l[i], prefix);
line = format_expand(ft, template); line = format_expand(ft, template);
if (single && tc != NULL && (~tc->flags & CLIENT_CONTROL)) if ((single && tc != NULL) || n == 1)
status_message_set(tc, -1, 1, 0, 0, "%s", line); status_message_set(tc, -1, 1, 0, 0, "%s", line);
else if (*line != '\0') else if (*line != '\0')
cmdq_print(item, "%s", line); cmdq_print(item, "%s", line);

View File

@@ -168,7 +168,6 @@ cmd_refresh_report(struct tty *tty, const char *value)
{ {
struct window_pane *wp; struct window_pane *wp;
u_int pane; u_int pane;
int fg, bg;
size_t size = 0; size_t size = 0;
char *copy, *split; char *copy, *split;
@@ -185,14 +184,8 @@ cmd_refresh_report(struct tty *tty, const char *value)
if (wp == NULL) if (wp == NULL)
goto out; goto out;
fg = wp->control_fg; tty_keys_colours(tty, split, strlen(split), &size, &wp->control_fg,
bg = wp->control_bg; &wp->control_bg);
if (tty_keys_colours(tty, split, strlen(split), &size, &fg, &bg) == 0) {
if (bg != wp->control_bg)
wp->flags |= PANE_THEMECHANGED;
wp->control_fg = fg;
wp->control_bg = bg;
}
out: out:
free(copy); free(copy);

View File

@@ -31,18 +31,18 @@ static enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_resize_pane_mouse_update(struct cmd *, static enum cmd_retval cmd_resize_pane_mouse_update(struct cmd *,
struct cmdq_item *); struct cmdq_item *);
static void cmd_resize_pane_mouse_resize_move_floating( static void cmd_resize_pane_mouse_update_floating(struct client *,
struct client *, struct mouse_event *); struct mouse_event *);
static void cmd_resize_pane_mouse_resize_tiled(struct client *, static void cmd_resize_pane_mouse_update_tiled(struct client *,
struct mouse_event *); struct mouse_event *);
const struct cmd_entry cmd_resize_pane_entry = { const struct cmd_entry cmd_resize_pane_entry = {
.name = "resize-pane", .name = "resize-pane",
.alias = "resizep", .alias = "resizep",
.args = { "D::L::MR::Tt:U::x:y:Z", 0, 1, NULL }, .args = { "DLMRTt:Ux:y:Z", 0, 1, NULL },
.usage = "[-MTZ] [-D lines] [-L columns] [-R columns] [-U lines] " .usage = "[-DLMRTUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " "
"[-x width] [-y height] " CMD_TARGET_PANE_USAGE, "[adjustment]",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -58,21 +58,17 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *wp = target->wp; struct window_pane *wp = target->wp;
struct winlink *wl = target->wl; struct winlink *wl = target->wl;
struct window *w = wl->window; struct window *w = wl->window;
struct layout_cell *lc = wp->layout_cell; const char *errstr;
enum layout_type type; char *cause;
const char *errstr, *argval; u_int adjust;
const char flags[4] = { 'U', 'D', 'L', 'R' }; int x, y, status;
char *cause = NULL, flag;
u_int opposite = 0;
int adjust, x, y, status;
long unsigned i;
struct grid *gd = wp->base.grid; struct grid *gd = wp->base.grid;
if (args_has(args, 'T')) { if (args_has(args, 'T')) {
if (!TAILQ_EMPTY(&wp->modes)) if (!TAILQ_EMPTY(&wp->modes))
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
adjust = screen_size_y(&wp->base) - 1 - wp->base.cy; adjust = screen_size_y(&wp->base) - 1 - wp->base.cy;
if (adjust > (int)gd->hsize) if (adjust > gd->hsize)
adjust = gd->hsize; adjust = gd->hsize;
grid_remove_history(gd, adjust); grid_remove_history(gd, adjust);
wp->base.cy += adjust; wp->base.cy += adjust;
@@ -93,32 +89,33 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
} }
server_unzoom_window(w); server_unzoom_window(w);
if (args_count(args) == 0)
adjust = 1;
else {
adjust = strtonum(args_string(args, 0), 1, INT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "adjustment %s", errstr);
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'x')) { if (args_has(args, 'x')) {
x = args_percentage(args, 'x', 0, PANE_MAXIMUM, w->sx, &cause); x = args_percentage(args, 'x', 0, INT_MAX, w->sx, &cause);
if (cause != NULL) { if (cause != NULL) {
cmdq_error(item, "width %s", cause); cmdq_error(item, "width %s", cause);
free(cause); free(cause);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (window_pane_is_floating(wp)) { layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x);
layout_resize_floating_pane_to(wp, LAYOUT_LEFTRIGHT, x,
&cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
} else
layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x);
} }
if (args_has(args, 'y')) { if (args_has(args, 'y')) {
y = args_percentage(args, 'y', 0, PANE_MAXIMUM, w->sy, &cause); y = args_percentage(args, 'y', 0, INT_MAX, w->sy, &cause);
if (cause != NULL) { if (cause != NULL) {
cmdq_error(item, "height %s", cause); cmdq_error(item, "height %s", cause);
free(cause); free(cause);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
status = window_get_pane_status(w); status = options_get_number(w->options, "pane-border-status");
switch (status) { switch (status) {
case PANE_STATUS_TOP: case PANE_STATUS_TOP:
if (y != INT_MAX && wp->yoff == 1) if (y != INT_MAX && wp->yoff == 1)
@@ -129,60 +126,18 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
y++; y++;
break; break;
} }
if (window_pane_is_floating(wp)) { layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y);
layout_resize_floating_pane_to(wp, LAYOUT_TOPBOTTOM, y,
&cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
} else
layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y);
} }
for (i = 0; i < nitems(flags); i++) { if (args_has(args, 'L'))
flag = flags[i]; layout_resize_pane(wp, LAYOUT_LEFTRIGHT, -adjust, 1);
if (!args_has(args, flag)) else if (args_has(args, 'R'))
continue; layout_resize_pane(wp, LAYOUT_LEFTRIGHT, adjust, 1);
else if (args_has(args, 'U'))
argval = args_get(args, flag); layout_resize_pane(wp, LAYOUT_TOPBOTTOM, -adjust, 1);
if (argval == NULL) else if (args_has(args, 'D'))
argval = "1"; layout_resize_pane(wp, LAYOUT_TOPBOTTOM, adjust, 1);
server_redraw_window(wl->window);
adjust = strtonum(argval, INT_MIN, INT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "adjustment %s", errstr);
return (CMD_RETURN_ERROR);
}
type = LAYOUT_TOPBOTTOM;
if (flag == 'L' || flag == 'R')
type = LAYOUT_LEFTRIGHT;
if (window_pane_is_floating(wp)) {
if (flag == 'L' || flag == 'U')
opposite = 1;
layout_resize_floating_pane(wp, type, adjust, opposite,
&cause);
if (cause != NULL) {
cmdq_error(item, "adjustment %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
} else {
if (flag == 'L' || flag == 'U')
adjust = -adjust;
layout_resize_pane(wp, type, adjust, 1);
}
}
if (lc->parent != NULL)
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
notify_window("window-layout-changed", w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
@@ -205,29 +160,21 @@ cmd_resize_pane_mouse_update(__unused struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
if (!window_pane_is_floating(wp)) { if (!window_pane_is_floating(wp)) {
c->tty.mouse_drag_update = cmd_resize_pane_mouse_resize_tiled; c->tty.mouse_drag_update = cmd_resize_pane_mouse_update_tiled;
cmd_resize_pane_mouse_resize_tiled(c, &event->m); cmd_resize_pane_mouse_update_tiled(c, &event->m);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
window_redraw_active_switch(w, wp); window_redraw_active_switch(w, wp);
window_set_active_pane(w, wp, 1); window_set_active_pane(w, wp, 1);
c->tty.mouse_drag_update = cmd_resize_pane_mouse_resize_move_floating; c->tty.mouse_drag_update = cmd_resize_pane_mouse_update_floating;
cmd_resize_pane_mouse_resize_move_floating(c, &event->m); cmd_resize_pane_mouse_update_floating(c, &event->m);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
/*
* Resizes or moves the pane by dragging. Resize a floating pane by dragging
* the borders or corners. Grabbing an edge only resizes that axis (special
* case). Moves the pane if dragging the top border. Since characters are
* generally rectangular, to make it easier to grab the corner, the character
* next to the corner is also considered the corner.
*/
static void static void
cmd_resize_pane_mouse_resize_move_floating(struct client *c, cmd_resize_pane_mouse_update_floating(struct client *c, struct mouse_event *m)
struct mouse_event *m)
{ {
struct winlink *wl; struct winlink *wl;
struct window *w; struct window *w;
@@ -353,7 +300,7 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
} }
static void static void
cmd_resize_pane_mouse_resize_tiled(struct client *c, struct mouse_event *m) cmd_resize_pane_mouse_update_tiled(struct client *c, struct mouse_event *m)
{ {
struct winlink *wl; struct winlink *wl;
struct window *w; struct window *w;

View File

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

View File

@@ -135,7 +135,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
} }
if (args_has(args, 'm') || args_has(args, 'M')) { if (args_has(args, 'm') || args_has(args, 'M')) {
if (args_has(args, 'm') && !window_pane_is_visible(wp)) if (args_has(args, 'm') && !window_pane_visible(wp))
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
if (server_check_marked()) if (server_check_marked())
lastwp = marked_pane.wp; lastwp = marked_pane.wp;

View File

@@ -70,7 +70,6 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
struct key_table *table = NULL; struct key_table *table = NULL;
struct key_binding *bd; struct key_binding *bd;
struct key_event *event; struct key_event *event;
struct cmdq_item *new_after = after;
if (args_has(args, 'K')) { if (args_has(args, 'K')) {
if (tc == NULL) if (tc == NULL)
@@ -78,16 +77,10 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
event = xcalloc(1, sizeof *event); event = xcalloc(1, sizeof *event);
event->key = key|KEYC_SENT; event->key = key|KEYC_SENT;
memset(&event->m, 0, sizeof event->m); memset(&event->m, 0, sizeof event->m);
if (after == NULL) { if (server_client_handle_key(tc, event) == 0) {
if (server_client_handle_key(tc, event) != 0) free(event->buf);
return (item); free(event);
} else {
if (server_client_handle_key_after(tc, event, after,
&new_after) != 0)
return (new_after);
} }
free(event->buf);
free(event);
return (item); return (item);
} }
@@ -236,10 +229,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
if (count == 0) { if (count == 0) {
if (args_has(args, 'N') || args_has(args, 'R')) if (args_has(args, 'N') || args_has(args, 'R'))
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
after = args_has(args, 'K') ? item : NULL;
for (; np != 0; np--) for (; np != 0; np--)
after = cmd_send_keys_inject_key(item, after, args, cmd_send_keys_inject_key(item, NULL, args, event->key);
event->key);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -19,7 +19,6 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <grp.h>
#include <pwd.h> #include <pwd.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -38,71 +37,67 @@ const struct cmd_entry cmd_server_access_entry = {
.name = "server-access", .name = "server-access",
.alias = NULL, .alias = NULL,
.args = { "adglrw", 0, 1, NULL }, .args = { "adlrw", 0, 1, NULL },
.usage = "[-adglrw] " CMD_TARGET_PANE_USAGE " [user|group]", .usage = "[-adlrw] " CMD_TARGET_PANE_USAGE " [user]",
.flags = CMD_CLIENT_CANFAIL, .flags = CMD_CLIENT_CANFAIL,
.exec = cmd_server_access_exec .exec = cmd_server_access_exec
}; };
static enum cmd_retval static enum cmd_retval
cmd_server_access_deny(struct cmdq_item *item, id_t id, int flags, cmd_server_access_deny(struct cmdq_item *item, struct passwd *pw)
const char *type, const char *name)
{ {
if (!server_acl_find(id, flags)) { struct client *loop;
cmdq_error(item, "%s %s not found", type, name); struct server_acl_user *user;
uid_t uid;
if ((user = server_acl_user_find(pw->pw_uid)) == NULL) {
cmdq_error(item, "user %s not found", pw->pw_name);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
server_acl_deny(id, flags); TAILQ_FOREACH(loop, &clients, entry) {
uid = proc_get_peer_uid(loop->peer);
if (uid == server_acl_get_uid(user)) {
loop->exit_message = xstrdup("access not allowed");
loop->flags |= CLIENT_EXIT;
}
}
server_acl_user_deny(pw->pw_uid);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
static enum cmd_retval static enum cmd_retval
cmd_server_access_exec(struct cmd *self, struct cmdq_item *item) cmd_server_access_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct client *c = cmdq_get_target_client(item); struct client *c = cmdq_get_target_client(item);
char *arg; char *name;
const char *name = NULL, *type; struct passwd *pw = NULL;
struct passwd *pw;
struct group *gr;
id_t id;
int flags = 0;
if (args_has(args, 'l')) { if (args_has(args, 'l')) {
server_acl_display(item); server_acl_display(item);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (args_count(args) == 0) { if (args_count(args) == 0) {
cmdq_error(item, "missing user or group argument"); cmdq_error(item, "missing user argument");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
arg = format_single(item, args_string(args, 0), c, NULL, NULL, NULL); name = format_single(item, args_string(args, 0), c, NULL, NULL, NULL);
if (args_has(args, 'g')) { if (*name != '\0')
type = "group"; pw = getpwnam(name);
if ((gr = getgrnam(arg)) != NULL) { if (pw == NULL) {
id = gr->gr_gid; cmdq_error(item, "unknown user: %s", name);
name = gr->gr_name; free(name);
flags |= SERVER_ACL_IS_GROUP;
}
} else {
type = "user";
if ((pw = getpwnam(arg)) != NULL) {
id = pw->pw_uid;
name = pw->pw_name;
}
}
if (name == NULL) {
cmdq_error(item, "unknown %s: %s", type, arg);
free(arg);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
free(arg); free(name);
if ((~flags & SERVER_ACL_IS_GROUP) && (id == 0 || id == getuid())) { if (pw->pw_uid == 0 || pw->pw_uid == getuid()) {
cmdq_error(item, "%s owns the server, can't change access", cmdq_error(item, "%s owns the server, can't change access",
name); pw->pw_name);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
@@ -116,35 +111,36 @@ cmd_server_access_exec(struct cmd *self, struct cmdq_item *item)
} }
if (args_has(args, 'd')) if (args_has(args, 'd'))
return (cmd_server_access_deny(item, id, flags, type, name)); return (cmd_server_access_deny(item, pw));
if (args_has(args, 'a')) { if (args_has(args, 'a')) {
if (server_acl_find(id, flags)) { if (server_acl_user_find(pw->pw_uid) != NULL) {
cmdq_error(item, "%s %s is already added", type, name); cmdq_error(item, "user %s is already added",
pw->pw_name);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
server_acl_allow(id, flags); server_acl_user_allow(pw->pw_uid);
/* Do not return - allow -r or -w with -a. */ /* Do not return - allow -r or -w with -a. */
} else if (args_has(args, 'r') || args_has(args, 'w')) { } else if (args_has(args, 'r') || args_has(args, 'w')) {
/* -r or -w implies -a if the entry does not exist. */ /* -r or -w implies -a if user does not exist. */
if (!server_acl_find(id, flags)) if (server_acl_user_find(pw->pw_uid) == NULL)
server_acl_allow(id, flags); server_acl_user_allow(pw->pw_uid);
} }
if (args_has(args, 'w')) { if (args_has(args, 'w')) {
if (!server_acl_find(id, flags)) { if (server_acl_user_find(pw->pw_uid) == NULL) {
cmdq_error(item, "%s %s not found", type, name); cmdq_error(item, "user %s not found", pw->pw_name);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
server_acl_allow_write(id, flags); server_acl_user_allow_write(pw->pw_uid);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (args_has(args, 'r')) { if (args_has(args, 'r')) {
if (!server_acl_find(id, flags)) { if (server_acl_user_find(pw->pw_uid) == NULL) {
cmdq_error(item, "%s %s not found", type, name); cmdq_error(item, "user %s not found", pw->pw_name);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
server_acl_deny_write(id, flags); server_acl_user_deny_write(pw->pw_uid);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

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

View File

@@ -38,12 +38,11 @@ const struct cmd_entry cmd_new_pane_entry = {
.name = "new-pane", .name = "new-pane",
.alias = "newp", .alias = "newp",
.args = { "bB:c:de:EfF:hIkl:Lm:p:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL }, .args = { "bc:de:EfF:hIkl:Lm:p:PR:s:S:t:vx:X:y:Y:Z", 0, -1, NULL },
.usage = "[-bdefhIklPvWZ] [-B border-lines] " .usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] "
"[-c start-directory] [-e environment] " "[-F format] [-l size] [-m message] [-p percentage] "
"[-F format] [-l size] [-m message] [-p percentage] "
"[-s style] [-S active-border-style] " "[-s style] [-S active-border-style] "
"[-R inactive-border-style] [-T title] [-x width] [-y height] " "[-R inactive-border-style] [-x width] [-y height] "
"[-X x-position] [-Y y-position] " CMD_TARGET_PANE_USAGE " " "[-X x-position] [-Y y-position] " CMD_TARGET_PANE_USAGE " "
"[shell-command [argument ...]]", "[shell-command [argument ...]]",
@@ -57,11 +56,11 @@ const struct cmd_entry cmd_split_window_entry = {
.name = "split-window", .name = "split-window",
.alias = "splitw", .alias = "splitw",
.args = { "bc:de:EfF:hIkl:m:p:PR:s:S:t:T:vWZ", 0, -1, NULL }, .args = { "bc:de:EfF:hIkl:m:p:PR:s:S:t:vZ", 0, -1, NULL },
.usage = "[-bdefhIklPvWZ] [-c start-directory] [-e environment] " .usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] "
"[-F format] [-l size] [-m message] [-p percentage] " "[-F format] [-l size] [-m message] [-p percentage] "
"[-s style] [-S active-border-style] " "[-s style] [-S active-border-style] "
"[-R inactive-border-style] [-T title] " CMD_TARGET_PANE_USAGE " " "[-R inactive-border-style] " CMD_TARGET_PANE_USAGE " "
"[shell-command [argument ...]]", "[shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -85,11 +84,9 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
struct layout_cell *lc = NULL; struct layout_cell *lc = NULL;
struct cmd_find_state fs; struct cmd_find_state fs;
int input, empty, is_floating, flags = 0; int input, empty, is_floating, flags = 0;
const char *template, *style, *value; const char *template, *style;
char *cause = NULL, *cp, *title; char *cause = NULL, *cp;
const struct options_table_entry *oe;
struct args_value *av; struct args_value *av;
enum pane_lines lines;
u_int count = args_count(args); u_int count = args_count(args);
if (cmd_get_entry(self) == &cmd_new_pane_entry) if (cmd_get_entry(self) == &cmd_new_pane_entry)
@@ -117,24 +114,12 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if (empty) if (empty)
flags |= SPAWN_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) if (is_floating)
lc = layout_get_floating_cell(item, args, lines, w, wp, &cause); lc = layout_get_floating_cell(item, args, w, wp, &cause);
else else
lc = layout_get_tiled_cell(item, args, w, wp, flags, &cause); lc = layout_get_tiled_cell(item, args, w, wp, flags, &cause);
if (cause != NULL) { if (cause != NULL) {
cmdq_error(item, "%s", cause); cmdq_error(item, "size or position %s", cause);
free(cause); free(cause);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
@@ -167,7 +152,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if ((new_wp = spawn_pane(&sc, &cause)) == NULL) { if ((new_wp = spawn_pane(&sc, &cause)) == NULL) {
cmdq_error(item, "create pane failed: %s", cause); cmdq_error(item, "create pane failed: %s", cause);
free(cause); free(cause);
goto fail; if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_ERROR);
} }
style = args_get(args, 's'); style = args_get(args, 's');
@@ -175,7 +163,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if (options_set_string(new_wp->options, "window-style", 0, if (options_set_string(new_wp->options, "window-style", 0,
"%s", style) == NULL) { "%s", style) == NULL) {
cmdq_error(item, "bad style: %s", style); cmdq_error(item, "bad style: %s", style);
goto fail; return (CMD_RETURN_ERROR);
} }
options_set_string(new_wp->options, "window-active-style", 0, options_set_string(new_wp->options, "window-active-style", 0,
"%s", style); "%s", style);
@@ -187,7 +175,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
if (options_set_string(new_wp->options, if (options_set_string(new_wp->options,
"pane-active-border-style", 0, "%s", style) == NULL) { "pane-active-border-style", 0, "%s", style) == NULL) {
cmdq_error(item, "bad active border style: %s", style); cmdq_error(item, "bad active border style: %s", style);
goto fail; return (CMD_RETURN_ERROR);
} }
} }
style = args_get(args, 'R'); style = args_get(args, 'R');
@@ -196,11 +184,9 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
"%s", style) == NULL) { "%s", style) == NULL) {
cmdq_error(item, "bad inactive border style: %s", cmdq_error(item, "bad inactive border style: %s",
style); style);
goto fail; return (CMD_RETURN_ERROR);
} }
} }
if (args_has(args, 'B'))
options_set_number(new_wp->options, "pane-border-lines", lines);
if (args_has(args, 'k') || args_has(args, 'm')) { if (args_has(args, 'k') || args_has(args, 'm')) {
options_set_number(new_wp->options, "remain-on-exit", 3); options_set_number(new_wp->options, "remain-on-exit", 3);
if (args_has(args, 'm')) { if (args_has(args, 'm')) {
@@ -209,12 +195,6 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
args_get(args, 'm')); args_get(args, 'm'));
} }
} }
if (args_has(args, 'T')) {
title = format_single_from_target(item, args_get(args, 'T'));
screen_set_title(&new_wp->base, title, 0);
notify_pane("pane-title-changed", new_wp);
free(title);
}
if (input) { if (input) {
switch (window_pane_start_input(new_wp, item, &cause)) { switch (window_pane_start_input(new_wp, item, &cause)) {
@@ -225,7 +205,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
window_remove_pane(wp->window, new_wp); window_remove_pane(wp->window, new_wp);
cmdq_error(item, "%s", cause); cmdq_error(item, "%s", cause);
free(cause); free(cause);
goto fail; if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_ERROR);
case 1: case 1:
input = 0; input = 0;
break; break;
@@ -256,24 +239,5 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
environ_free(sc.environ); environ_free(sc.environ);
if (input) if (input)
return (CMD_RETURN_WAIT); return (CMD_RETURN_WAIT);
if (args_has(args, 'W')) {
/*
* With -W, block this command queue item until the pane's
* command exits; window_pane_wait_finish will be called to
* continue it.
*/
new_wp->wait_item = item;
return (CMD_RETURN_WAIT);
}
return (CMD_RETURN_NORMAL); 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

@@ -114,6 +114,10 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
dst_wp->layout_cell = src_lc; dst_wp->layout_cell = src_lc;
dst_lc->wp = src_wp; dst_lc->wp = src_wp;
src_wp->layout_cell = dst_lc; src_wp->layout_cell = dst_lc;
if (window_pane_is_floating(src_wp) != window_pane_is_floating(dst_wp)) {
src_wp->layout_cell->flags ^= LAYOUT_CELL_FLOATING;
dst_wp->layout_cell->flags ^= LAYOUT_CELL_FLOATING;
}
src_wp->window = dst_w; src_wp->window = dst_w;
options_set_parent(src_wp->options, dst_w->options); options_set_parent(src_wp->options, dst_w->options);
@@ -149,11 +153,9 @@ 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(&src_wp->palette, src_wp->options);
colour_palette_from_option(&dst_wp->palette, dst_wp->options); colour_palette_from_option(&dst_wp->palette, dst_wp->options);
layout_fix_panes(src_w, NULL); layout_fix_panes(src_w, NULL);
redraw_invalidate_scene(src_w);
server_redraw_window(src_w); server_redraw_window(src_w);
} }
layout_fix_panes(dst_w, NULL); layout_fix_panes(dst_w, NULL);
redraw_invalidate_scene(dst_w);
server_redraw_window(dst_w); server_redraw_window(dst_w);
notify_window("window-layout-changed", src_w); notify_window("window-layout-changed", src_w);

View File

@@ -120,28 +120,6 @@ colour_force_rgb(int c)
return (-1); return (-1);
} }
/* Dim colour by a percentage. */
int
colour_dim(int c, u_int dim)
{
u_char r, g, b;
if (dim == 0 || COLOUR_DEFAULT(c))
return (c);
if (dim >= 100)
return (colour_join_rgb(0, 0, 0));
c = colour_force_rgb(c);
if (c == -1)
return (-1);
colour_split_rgb(c, &r, &g, &b);
r = (r * (100 - dim)) / 100;
g = (g * (100 - dim)) / 100;
b = (b * (100 - dim)) / 100;
return (colour_join_rgb(r, g, b));
}
/* Convert colour to a string. */ /* Convert colour to a string. */
const char * const char *
colour_tostring(int c) colour_tostring(int c)

View File

@@ -339,7 +339,7 @@ static int
getopt_internal(int nargc, char * const *nargv, const char *options, getopt_internal(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx, int flags) const struct option *long_options, int *idx, int flags)
{ {
const char *oli; /* option letter list index */ char *oli; /* option letter list index */
int optchar, short_too; int optchar, short_too;
static int posixly_correct = -1; static int posixly_correct = -1;

View File

@@ -1,6 +1,6 @@
# configure.ac # configure.ac
AC_INIT([tmux], next-3.7) AC_INIT([tmux], 3.7)
AC_PREREQ([2.60]) AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc) AC_CONFIG_AUX_DIR(etc)

View File

@@ -224,14 +224,6 @@ control_notify_session_window_changed(struct session *s)
{ {
struct client *c; struct client *c;
/*
* A deferred session-window-changed notification can fire after the
* session has been destroyed (which sets curw to NULL) but is kept
* alive by the notification's reference. Skip the notification.
*/
if (s->curw == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue; continue;

View File

@@ -152,8 +152,7 @@ environ_clear(struct environ *env, const char *name)
void void
environ_put(struct environ *env, const char *var, int flags) environ_put(struct environ *env, const char *var, int flags)
{ {
char *name; char *name, *value;
const char *value;
value = strchr(var, '='); value = strchr(var, '=');
if (value == NULL) if (value == NULL)

320
format.c
View File

@@ -117,10 +117,6 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
#define FORMAT_NOT_NOT 0x100000 #define FORMAT_NOT_NOT 0x100000
#define FORMAT_REPEAT 0x200000 #define FORMAT_REPEAT 0x200000
#define FORMAT_QUOTE_ARGUMENTS 0x400000 #define FORMAT_QUOTE_ARGUMENTS 0x400000
#define FORMAT_RELATIVE 0x800000
#define FORMAT_CLIENT_TERMCAP 0x1000000
#define FORMAT_CLIENT_TERMFEAT 0x2000000
#define FORMAT_CLIENT_ENVIRON 0x4000000
/* Limit on recursion. */ /* Limit on recursion. */
#define FORMAT_LOOP_LIMIT 100 #define FORMAT_LOOP_LIMIT 100
@@ -128,9 +124,6 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
/* Limit on time taken (milliseconds). */ /* Limit on time taken (milliseconds). */
#define FORMAT_TIME_LIMIT 100 #define FORMAT_TIME_LIMIT 100
/* How often to check the time in long loops. */
#define FORMAT_TIME_LOOP_CHECK 10000
/* Format expand flags. */ /* Format expand flags. */
#define FORMAT_EXPAND_TIME 0x1 #define FORMAT_EXPAND_TIME 0x1
#define FORMAT_EXPAND_NOJOBS 0x2 #define FORMAT_EXPAND_NOJOBS 0x2
@@ -576,7 +569,7 @@ format_cb_session_attached_list(struct format_tree *ft)
} }
if ((size = EVBUFFER_LENGTH(buffer)) != 0) if ((size = EVBUFFER_LENGTH(buffer)) != 0)
value = xmemdup(EVBUFFER_DATA(buffer), size); xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
evbuffer_free(buffer); evbuffer_free(buffer);
return (value); return (value);
} }
@@ -715,7 +708,7 @@ format_cb_window_linked_sessions_list(struct format_tree *ft)
} }
if ((size = EVBUFFER_LENGTH(buffer)) != 0) if ((size = EVBUFFER_LENGTH(buffer)) != 0)
value = xmemdup(EVBUFFER_DATA(buffer), size); xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
evbuffer_free(buffer); evbuffer_free(buffer);
return (value); return (value);
} }
@@ -769,7 +762,7 @@ format_cb_window_active_sessions_list(struct format_tree *ft)
} }
if ((size = EVBUFFER_LENGTH(buffer)) != 0) if ((size = EVBUFFER_LENGTH(buffer)) != 0)
value = xmemdup(EVBUFFER_DATA(buffer), size); xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
evbuffer_free(buffer); evbuffer_free(buffer);
return (value); return (value);
} }
@@ -833,7 +826,7 @@ format_cb_window_active_clients_list(struct format_tree *ft)
} }
if ((size = EVBUFFER_LENGTH(buffer)) != 0) if ((size = EVBUFFER_LENGTH(buffer)) != 0)
value = xmemdup(EVBUFFER_DATA(buffer), size); xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
evbuffer_free(buffer); evbuffer_free(buffer);
return (value); return (value);
} }
@@ -1008,7 +1001,7 @@ format_cb_pane_tabs(struct format_tree *ft)
evbuffer_add_printf(buffer, "%u", i); evbuffer_add_printf(buffer, "%u", i);
} }
if ((size = EVBUFFER_LENGTH(buffer)) != 0) if ((size = EVBUFFER_LENGTH(buffer)) != 0)
value = xmemdup(EVBUFFER_DATA(buffer), size); xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
evbuffer_free(buffer); evbuffer_free(buffer);
return (value); return (value);
} }
@@ -1023,7 +1016,7 @@ format_cb_pane_fg(struct format_tree *ft)
if (wp == NULL) if (wp == NULL)
return (NULL); return (NULL);
tty_default_colours(&gc, wp, NULL); tty_default_colours(&gc, wp);
return (xstrdup(colour_tostring(gc.fg))); return (xstrdup(colour_tostring(gc.fg)));
} }
@@ -1060,7 +1053,7 @@ format_cb_pane_bg(struct format_tree *ft)
if (wp == NULL) if (wp == NULL)
return (NULL); return (NULL);
tty_default_colours(&gc, wp, NULL); tty_default_colours(&gc, wp);
return (xstrdup(colour_tostring(gc.bg))); return (xstrdup(colour_tostring(gc.bg)));
} }
@@ -1092,7 +1085,7 @@ format_cb_session_group_list(struct format_tree *ft)
} }
if ((size = EVBUFFER_LENGTH(buffer)) != 0) if ((size = EVBUFFER_LENGTH(buffer)) != 0)
value = xmemdup(EVBUFFER_DATA(buffer), size); xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
evbuffer_free(buffer); evbuffer_free(buffer);
return (value); return (value);
} }
@@ -1132,7 +1125,7 @@ format_cb_session_group_attached_list(struct format_tree *ft)
} }
if ((size = EVBUFFER_LENGTH(buffer)) != 0) if ((size = EVBUFFER_LENGTH(buffer)) != 0)
value = xmemdup(EVBUFFER_DATA(buffer), size); xasprintf(&value, "%.*s", size, EVBUFFER_DATA(buffer));
evbuffer_free(buffer); evbuffer_free(buffer);
return (value); return (value);
} }
@@ -1160,13 +1153,15 @@ static void *
format_cb_pane_at_top(struct format_tree *ft) format_cb_pane_at_top(struct format_tree *ft)
{ {
struct window_pane *wp = ft->wp; struct window_pane *wp = ft->wp;
struct window *w;
int status, flag; int status, flag;
char *value; char *value;
if (wp == NULL) if (wp == NULL)
return (NULL); return (NULL);
w = wp->window;
status = window_pane_get_pane_status(wp); status = options_get_number(w->options, "pane-border-status");
if (status == PANE_STATUS_TOP) if (status == PANE_STATUS_TOP)
flag = (wp->yoff == 1); flag = (wp->yoff == 1);
else else
@@ -1188,7 +1183,7 @@ format_cb_pane_at_bottom(struct format_tree *ft)
return (NULL); return (NULL);
w = wp->window; w = wp->window;
status = window_pane_get_pane_status(wp); status = options_get_number(w->options, "pane-border-status");
if (status == PANE_STATUS_BOTTOM) if (status == PANE_STATUS_BOTTOM)
flag = (wp->yoff + (int)wp->sy == (int)w->sy - 1); flag = (wp->yoff + (int)wp->sy == (int)w->sy - 1);
else else
@@ -1477,33 +1472,6 @@ format_cb_client_cell_width(struct format_tree *ft)
return (NULL); 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. */ /* Callback for client_control_mode. */
static void * static void *
format_cb_client_control_mode(struct format_tree *ft) format_cb_client_control_mode(struct format_tree *ft)
@@ -1883,6 +1851,15 @@ format_cb_keypad_flag(struct format_tree *ft)
return (NULL); return (NULL);
} }
/* Callback for loop_last_flag. */
static void *
format_cb_loop_last_flag(struct format_tree *ft)
{
if (ft->flags & FORMAT_LAST)
return (xstrdup("1"));
return (xstrdup("0"));
}
/* Callback for mouse_all_flag. */ /* Callback for mouse_all_flag. */
static void * static void *
format_cb_mouse_all_flag(struct format_tree *ft) format_cb_mouse_all_flag(struct format_tree *ft)
@@ -3241,9 +3218,6 @@ static const struct format_table_entry format_table[] = {
{ "client_cell_width", FORMAT_TABLE_STRING, { "client_cell_width", FORMAT_TABLE_STRING,
format_cb_client_cell_width format_cb_client_cell_width
}, },
{ "client_colours", FORMAT_TABLE_STRING,
format_cb_client_colours
},
{ "client_control_mode", FORMAT_TABLE_STRING, { "client_control_mode", FORMAT_TABLE_STRING,
format_cb_client_control_mode format_cb_client_control_mode
}, },
@@ -3370,6 +3344,9 @@ static const struct format_table_entry format_table[] = {
{ "last_window_index", FORMAT_TABLE_STRING, { "last_window_index", FORMAT_TABLE_STRING,
format_cb_last_window_index format_cb_last_window_index
}, },
{ "loop_last_flag", FORMAT_TABLE_STRING,
format_cb_loop_last_flag
},
{ "mouse_all_flag", FORMAT_TABLE_STRING, { "mouse_all_flag", FORMAT_TABLE_STRING,
format_cb_mouse_all_flag format_cb_mouse_all_flag
}, },
@@ -3797,7 +3774,7 @@ format_table_compare(const void *key0, const void *entry0)
} }
/* Get a format callback. */ /* Get a format callback. */
static const struct format_table_entry * static struct format_table_entry *
format_table_get(const char *key) format_table_get(const char *key)
{ {
return (bsearch(key, format_table, nitems(format_table), return (bsearch(key, format_table, nitems(format_table),
@@ -4086,52 +4063,12 @@ format_pretty_time(time_t t, int seconds)
return (xstrdup(s)); return (xstrdup(s));
} }
/* Make a relative time. */
static char *
format_relative_time(time_t t)
{
time_t now, age;
u_int d, h, m, s;
char out[32];
time(&now);
if (t > now)
return (NULL);
if (t == now)
return (xstrdup("0s"));
age = now - t;
d = age / 86400;
h = (age % 86400) / 3600;
m = (age % 3600) / 60;
s = age % 60;
if (d != 0) {
if (h != 0)
xsnprintf(out, sizeof out, "%ud%uh", d, h);
else
xsnprintf(out, sizeof out, "%ud", d);
} else if (h != 0) {
if (m != 0)
xsnprintf(out, sizeof out, "%uh%um", h, m);
else
xsnprintf(out, sizeof out, "%uh", h);
} else if (m != 0) {
if (s != 0)
xsnprintf(out, sizeof out, "%um%us", m, s);
else
xsnprintf(out, sizeof out, "%um", m);
} else
xsnprintf(out, sizeof out, "%us", s);
return (xstrdup(out));
}
/* Find a format entry. */ /* Find a format entry. */
static char * static char *
format_find(struct format_tree *ft, const char *key, int modifiers, format_find(struct format_tree *ft, const char *key, int modifiers,
const char *time_format) const char *time_format)
{ {
const struct format_table_entry *fte; struct format_table_entry *fte;
void *value; void *value;
struct format_entry *fe, fe_find; struct format_entry *fe, fe_find;
struct environ_entry *envent; struct environ_entry *envent;
@@ -4207,9 +4144,7 @@ found:
} }
if (t == 0) if (t == 0)
return (NULL); return (NULL);
if (modifiers & FORMAT_RELATIVE) if (modifiers & FORMAT_PRETTY)
found = format_relative_time(t);
else if (modifiers & FORMAT_PRETTY)
found = format_pretty_time(t, 0); found = format_pretty_time(t, 0);
else { else {
if (time_format != NULL) { if (time_format != NULL) {
@@ -4258,14 +4193,10 @@ found:
/* Check if format has not taken too long. */ /* Check if format has not taken too long. */
static int static int
format_check_time(struct format_expand_state *es, u_int *check) format_check_time(struct format_expand_state *es)
{ {
uint64_t t; uint64_t t = get_timer();
if (check != NULL && ++*check % FORMAT_TIME_LOOP_CHECK != 0)
return (1);
t = get_timer();
if (t - es->start_time < FORMAT_TIME_LIMIT) if (t - es->start_time < FORMAT_TIME_LIMIT)
return (1); return (1);
t -= es->start_time; t -= es->start_time;
@@ -4276,24 +4207,21 @@ format_check_time(struct format_expand_state *es, u_int *check)
/* Unescape escaped characters. */ /* Unescape escaped characters. */
static char * static char *
format_unescape(struct format_expand_state *es, const char *s, size_t n) format_unescape(struct format_expand_state *es, const char *s)
{ {
const char *end = s + n; char *out, *cp;
char *out, *cp; int brackets = 0;
int brackets = 0;
u_int check = 0;
cp = out = xmalloc(n + 1); cp = out = xmalloc(strlen(s) + 1);
for (; s != end; s++) { for (; *s != '\0'; s++) {
if (!format_check_time(es, &check)) { if (!format_check_time(es)){
free(out); free(out);
return (xstrdup("")); return (xstrdup(""));
} }
if (*s == '#' && s + 1 != end && s[1] == '{') if (*s == '#' && s[1] == '{')
brackets++; brackets++;
if (brackets == 0 && if (brackets == 0 &&
*s == '#' && *s == '#' &&
s + 1 != end &&
strchr(",#{}:", s[1]) != NULL) { strchr(",#{}:", s[1]) != NULL) {
*cp++ = *++s; *cp++ = *++s;
continue; continue;
@@ -4312,11 +4240,10 @@ format_strip(struct format_expand_state *es, const char *s)
{ {
char *out, *cp; char *out, *cp;
int brackets = 0; int brackets = 0;
u_int check = 0;
cp = out = xmalloc(strlen(s) + 1); cp = out = xmalloc(strlen(s) + 1);
for (; *s != '\0'; s++) { for (; *s != '\0'; s++) {
if (!format_check_time(es, &check)) { if (!format_check_time(es)){
free(out); free(out);
return (xstrdup("")); return (xstrdup(""));
} }
@@ -4340,10 +4267,9 @@ static const char *
format_skip1(struct format_expand_state *es, const char *s, const char *end) format_skip1(struct format_expand_state *es, const char *s, const char *end)
{ {
int brackets = 0; int brackets = 0;
u_int check = 0;
for (; *s != '\0'; s++) { for (; *s != '\0'; s++) {
if (es != NULL && !format_check_time(es, &check)) if (es != NULL && !format_check_time(es))
return (NULL); return (NULL);
if (*s == '#' && s[1] == '{') if (*s == '#' && s[1] == '{')
brackets++; brackets++;
@@ -4453,7 +4379,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
/* /*
* Modifiers are a ; separated list of the forms: * Modifiers are a ; separated list of the forms:
* l,m,C,a,b,c,d,I,n,t,w,q,E,T,S,W,P,R,<,> * l,m,C,a,b,c,d,n,t,w,q,E,T,S,W,P,R,<,>
* =a * =a
* =/a * =/a
* =/a/ * =/a/
@@ -4494,7 +4420,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
} }
/* Now try single character with arguments. */ /* Now try single character with arguments. */
if (strchr("ImCLNPSst=pReqW", cp[0]) == NULL) if (strchr("mCLNPSst=pReqW", cp[0]) == NULL)
break; break;
c = cp[0]; c = cp[0];
@@ -4514,7 +4440,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
break; break;
argv = xcalloc(1, sizeof *argv); argv = xcalloc(1, sizeof *argv);
value = format_unescape(es, cp + 1, end - (cp + 1)); value = xstrndup(cp + 1, end - (cp + 1));
argv[0] = format_expand1(es, value); argv[0] = format_expand1(es, value);
free(value); free(value);
argc = 1; argc = 1;
@@ -4538,7 +4464,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
cp++; cp++;
argv = xreallocarray(argv, argc + 1, sizeof *argv); argv = xreallocarray(argv, argc + 1, sizeof *argv);
value = format_unescape(es, cp, end - cp); value = xstrndup(cp, end - cp);
argv[argc++] = format_expand1(es, value); argv[argc++] = format_expand1(es, value);
free(value); free(value);
@@ -4701,19 +4627,17 @@ format_loop_sessions(struct format_expand_state *es, const char *fmt)
struct format_tree *nft; struct format_tree *nft;
struct format_expand_state next; struct format_expand_state next;
char *all, *active, *use, *expanded, *value; char *all, *active, *use, *expanded, *value;
struct evbuffer *buffer; size_t valuelen;
size_t size;
struct session *s, **l; struct session *s, **l;
int i, n; int i, n, last = 0;
if (format_choose(es, fmt, &all, &active, 0) != 0) { if (format_choose(es, fmt, &all, &active, 0) != 0) {
all = xstrdup(fmt); all = xstrdup(fmt);
active = NULL; active = NULL;
} }
buffer = evbuffer_new(); value = xcalloc(1, 1);
if (buffer == NULL) valuelen = 1;
fatalx("out of memory");
l = sort_get_sessions(&n, sc); l = sort_get_sessions(&n, sc);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
@@ -4725,27 +4649,25 @@ format_loop_sessions(struct format_expand_state *es, const char *fmt)
use = active; use = active;
else else
use = all; use = all;
nft = format_create(c, item, FORMAT_NONE, ft->flags); if (i == n - 1)
format_add(nft, "loop_index", "%d", i); last = FORMAT_LAST;
format_add(nft, "loop_last_flag", "%d", i == n - 1); nft = format_create(c, item, FORMAT_NONE, ft->flags|last);
format_defaults(nft, ft->c, s, NULL, NULL); format_defaults(nft, ft->c, s, NULL, NULL);
format_copy_state(&next, es, 0); format_copy_state(&next, es, 0);
next.ft = nft; next.ft = nft;
expanded = format_expand1(&next, use); expanded = format_expand1(&next, use);
format_free(next.ft); format_free(next.ft);
evbuffer_add(buffer, expanded, strlen(expanded)); valuelen += strlen(expanded);
value = xrealloc(value, valuelen);
strlcat(value, expanded, valuelen);
free(expanded); free(expanded);
} }
free(active); free(active);
free(all); free(all);
if ((size = EVBUFFER_LENGTH(buffer)) != 0)
value = xmemdup(EVBUFFER_DATA(buffer), size);
else
value = xstrdup("");
evbuffer_free(buffer);
return (value); return (value);
} }
@@ -4815,11 +4737,10 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
struct format_tree *nft; struct format_tree *nft;
struct format_expand_state next; struct format_expand_state next;
char *all, *active, *use, *expanded, *value; char *all, *active, *use, *expanded, *value;
struct evbuffer *buffer; size_t valuelen;
size_t size;
struct winlink *wl, **l; struct winlink *wl, **l;
struct window *w; struct window *w;
int i, n; int i, n, last = 0;
if (ft->s == NULL) { if (ft->s == NULL) {
format_log(es, "window loop but no session"); format_log(es, "window loop but no session");
@@ -4831,9 +4752,8 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
active = NULL; active = NULL;
} }
buffer = evbuffer_new(); value = xcalloc(1, 1);
if (buffer == NULL) valuelen = 1;
fatalx("out of memory");
l = sort_get_winlinks_session(ft->s, &n, sc); l = sort_get_winlinks_session(ft->s, &n, sc);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
@@ -4844,10 +4764,10 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
use = active; use = active;
else else
use = all; use = all;
if (i == n - 1)
last = FORMAT_LAST;
nft = format_create(c, item, FORMAT_WINDOW|w->id, nft = format_create(c, item, FORMAT_WINDOW|w->id,
ft->flags); ft->flags|last);
format_add(nft, "loop_index", "%d", i);
format_add(nft, "loop_last_flag", "%d", i == n - 1);
format_defaults(nft, ft->c, ft->s, wl, NULL); format_defaults(nft, ft->c, ft->s, wl, NULL);
/* Add neighbor window data to the format tree. */ /* Add neighbor window data to the format tree. */
@@ -4865,18 +4785,16 @@ format_loop_windows(struct format_expand_state *es, const char *fmt)
expanded = format_expand1(&next, use); expanded = format_expand1(&next, use);
format_free(nft); format_free(nft);
evbuffer_add(buffer, expanded, strlen(expanded)); valuelen += strlen(expanded);
value = xrealloc(value, valuelen);
strlcat(value, expanded, valuelen);
free(expanded); free(expanded);
} }
free(active); free(active);
free(all); free(all);
if ((size = EVBUFFER_LENGTH(buffer)) != 0)
value = xmemdup(EVBUFFER_DATA(buffer), size);
else
value = xstrdup("");
evbuffer_free(buffer);
return (value); return (value);
} }
@@ -4891,10 +4809,9 @@ format_loop_panes(struct format_expand_state *es, const char *fmt)
struct format_tree *nft; struct format_tree *nft;
struct format_expand_state next; struct format_expand_state next;
char *all, *active, *use, *expanded, *value; char *all, *active, *use, *expanded, *value;
struct evbuffer *buffer; size_t valuelen;
size_t size;
struct window_pane *wp, **l; struct window_pane *wp, **l;
int i, n; int i, n, last = 0;
if (ft->w == NULL) { if (ft->w == NULL) {
format_log(es, "pane loop but no window"); format_log(es, "pane loop but no window");
@@ -4906,9 +4823,8 @@ format_loop_panes(struct format_expand_state *es, const char *fmt)
active = NULL; active = NULL;
} }
buffer = evbuffer_new(); value = xcalloc(1, 1);
if (buffer == NULL) valuelen = 1;
fatalx("out of memory");
l = sort_get_panes_window(ft->w, &n, sc); l = sort_get_panes_window(ft->w, &n, sc);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
@@ -4918,28 +4834,26 @@ format_loop_panes(struct format_expand_state *es, const char *fmt)
use = active; use = active;
else else
use = all; use = all;
if (i == n - 1)
last = FORMAT_LAST;
nft = format_create(c, item, FORMAT_PANE|wp->id, nft = format_create(c, item, FORMAT_PANE|wp->id,
ft->flags); ft->flags|last);
format_add(nft, "loop_index", "%d", i);
format_add(nft, "loop_last_flag", "%d", i == n - 1);
format_defaults(nft, ft->c, ft->s, ft->wl, wp); format_defaults(nft, ft->c, ft->s, ft->wl, wp);
format_copy_state(&next, es, 0); format_copy_state(&next, es, 0);
next.ft = nft; next.ft = nft;
expanded = format_expand1(&next, use); expanded = format_expand1(&next, use);
format_free(nft); format_free(nft);
evbuffer_add(buffer, expanded, strlen(expanded)); valuelen += strlen(expanded);
value = xrealloc(value, valuelen);
strlcat(value, expanded, valuelen);
free(expanded); free(expanded);
} }
free(active); free(active);
free(all); free(all);
if ((size = EVBUFFER_LENGTH(buffer)) != 0)
value = xmemdup(EVBUFFER_DATA(buffer), size);
else
value = xstrdup("");
evbuffer_free(buffer);
return (value); return (value);
} }
@@ -4954,36 +4868,32 @@ format_loop_clients(struct format_expand_state *es, const char *fmt)
struct format_tree *nft; struct format_tree *nft;
struct format_expand_state next; struct format_expand_state next;
char *expanded, *value; char *expanded, *value;
struct evbuffer *buffer; size_t valuelen;
size_t size; int i, n, last = 0;
int i, n;
buffer = evbuffer_new(); value = xcalloc(1, 1);
if (buffer == NULL) valuelen = 1;
fatalx("out of memory");
l = sort_get_clients(&n, sc); l = sort_get_clients(&n, sc);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
c = l[i]; c = l[i];
format_log(es, "client loop: %s", c->name); format_log(es, "client loop: %s", c->name);
nft = format_create(c, item, 0, ft->flags); if (i == n - 1)
format_add(nft, "loop_index", "%d", i); last = FORMAT_LAST;
format_add(nft, "loop_last_flag", "%d", i == n - 1); nft = format_create(c, item, 0, ft->flags|last);
format_defaults(nft, c, ft->s, ft->wl, ft->wp); format_defaults(nft, c, ft->s, ft->wl, ft->wp);
format_copy_state(&next, es, 0); format_copy_state(&next, es, 0);
next.ft = nft; next.ft = nft;
expanded = format_expand1(&next, fmt); expanded = format_expand1(&next, fmt);
format_free(nft); format_free(nft);
evbuffer_add(buffer, expanded, strlen(expanded)); valuelen += strlen(expanded);
value = xrealloc(value, valuelen);
strlcat(value, expanded, valuelen);
free(expanded); free(expanded);
} }
if ((size = EVBUFFER_LENGTH(buffer)) != 0)
value = xmemdup(EVBUFFER_DATA(buffer), size);
else
value = xstrdup("");
evbuffer_free(buffer);
return (value); return (value);
} }
@@ -5149,9 +5059,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
struct format_modifier *list, *cmp = NULL, *search = NULL; struct format_modifier *list, *cmp = NULL, *search = NULL;
struct format_modifier **sub = NULL, *mexp = NULL, *fm; struct format_modifier **sub = NULL, *mexp = NULL, *fm;
struct format_modifier *bool_op_n = NULL; struct format_modifier *bool_op_n = NULL;
u_int i, count, nsub = 0, nrep, check = 0; u_int i, count, nsub = 0, nrep;
struct format_expand_state next; struct format_expand_state next;
struct environ_entry *envent;
/* Set sorting defaults. */ /* Set sorting defaults. */
sc->order = SORT_ORDER; sc->order = SORT_ORDER;
@@ -5234,29 +5143,16 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
case 'n': case 'n':
modifiers |= FORMAT_LENGTH; modifiers |= FORMAT_LENGTH;
break; break;
case 'I':
if (fm->argc < 1)
break;
if (strchr(fm->argv[0], 'f') != NULL)
modifiers |= FORMAT_CLIENT_TERMFEAT;
if (strchr(fm->argv[0], 'c') != NULL)
modifiers |= FORMAT_CLIENT_TERMCAP;
if (strchr(fm->argv[0], 'e') != NULL)
modifiers |= FORMAT_CLIENT_ENVIRON;
break;
case 't': case 't':
modifiers |= FORMAT_TIMESTRING; modifiers |= FORMAT_TIMESTRING;
if (fm->argc < 1) if (fm->argc < 1)
break; break;
if (strchr(fm->argv[0], 'p') != NULL) if (strchr(fm->argv[0], 'p') != NULL)
modifiers |= FORMAT_PRETTY; modifiers |= FORMAT_PRETTY;
else if (strchr(fm->argv[0], 'r') != NULL)
modifiers |= FORMAT_RELATIVE;
else if (fm->argc >= 2 && else if (fm->argc >= 2 &&
strchr(fm->argv[0], 'f') != NULL) { strchr(fm->argv[0], 'f') != NULL) {
free(time_format); free(time_format);
time_format = format_strip(es, time_format = format_strip(es, fm->argv[1]);
fm->argv[1]);
} }
break; break;
case 'q': case 'q':
@@ -5371,42 +5267,10 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
} }
} }
/* Look up client capability, feature or environment. */
if ((modifiers & FORMAT_CLIENT_TERMCAP) ||
(modifiers & FORMAT_CLIENT_TERMFEAT) ||
(modifiers & FORMAT_CLIENT_ENVIRON)) {
if (ft->c == NULL ||
ft->c->tty.term == NULL ||
ft->c->flags & CLIENT_UNATTACHEDFLAGS) {
value = xstrdup("");
goto done;
}
if (modifiers & FORMAT_CLIENT_TERMCAP) {
if (tty_term_has_name(ft->c->tty.term, copy))
value = xstrdup("1");
else
value = xstrdup("0");
}
if (modifiers & FORMAT_CLIENT_TERMFEAT) {
if (tty_feature_present(ft->c->tty.term, copy))
value = xstrdup("1");
else
value = xstrdup("0");
}
if (modifiers & FORMAT_CLIENT_ENVIRON) {
envent = environ_find(ft->c->environ, copy);
if (envent != NULL && envent->value != NULL)
value = xstrdup(envent->value);
else
value = xstrdup("");
}
goto done;
}
/* Is this a literal string? */ /* Is this a literal string? */
if (modifiers & FORMAT_LITERAL) { if (modifiers & FORMAT_LITERAL) {
format_log(es, "literal string is '%s'", copy); format_log(es, "literal string is '%s'", copy);
value = format_unescape(es, copy, strlen(copy)); value = format_unescape(es, copy);
goto done; goto done;
} }
@@ -5482,7 +5346,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
else { else {
value = xstrdup(""); value = xstrdup("");
for (i = 0; i < nrep; i++) { for (i = 0; i < nrep; i++) {
if (!format_check_time(es, &check)) { if (!format_check_time(es)) {
free(right); free(right);
free(left); free(left);
free(value); free(value);
@@ -5762,7 +5626,7 @@ format_expand1(struct format_expand_state *es, const char *fmt)
int ch, brackets; int ch, brackets;
char expanded[8192]; char expanded[8192];
if (fmt == NULL || *fmt == '\0' || !format_check_time(es, NULL)) if (fmt == NULL || *fmt == '\0' || !format_check_time(es))
return (xstrdup("")); return (xstrdup(""));
if (es->loop == FORMAT_LOOP_LIMIT) { if (es->loop == FORMAT_LOOP_LIMIT) {

11
grid.c
View File

@@ -292,7 +292,7 @@ grid_free_line(struct grid *gd, u_int py)
} }
/* Free several lines. */ /* Free several lines. */
void static void
grid_free_lines(struct grid *gd, u_int py, u_int ny) grid_free_lines(struct grid *gd, u_int py, u_int ny)
{ {
u_int yy; u_int yy;
@@ -320,10 +320,6 @@ grid_create(u_int sx, u_int sy, u_int hlimit)
gd->hsize = 0; gd->hsize = 0;
gd->hlimit = hlimit; gd->hlimit = hlimit;
gd->scroll_added = 0;
gd->scroll_collected = 0;
gd->scroll_generation = 0;
if (gd->sy != 0) if (gd->sy != 0)
gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata); gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
else else
@@ -409,7 +405,6 @@ grid_collect_history(struct grid *gd, int all)
grid_trim_history(gd, ny); grid_trim_history(gd, ny);
gd->hsize -= ny; gd->hsize -= ny;
gd->scroll_collected += ny;
if (gd->hscrolled > gd->hsize) if (gd->hscrolled > gd->hsize)
gd->hscrolled = gd->hsize; gd->hscrolled = gd->hsize;
} }
@@ -447,7 +442,6 @@ grid_scroll_history(struct grid *gd, u_int bg)
grid_compact_line(&gd->linedata[gd->hsize]); grid_compact_line(&gd->linedata[gd->hsize]);
gd->linedata[gd->hsize].time = current_time; gd->linedata[gd->hsize].time = current_time;
gd->hsize++; gd->hsize++;
gd->scroll_added++;
} }
/* Clear the history. */ /* Clear the history. */
@@ -458,7 +452,6 @@ grid_clear_history(struct grid *gd)
gd->hscrolled = 0; gd->hscrolled = 0;
gd->hsize = 0; gd->hsize = 0;
gd->scroll_generation++;
gd->linedata = xreallocarray(gd->linedata, gd->sy, gd->linedata = xreallocarray(gd->linedata, gd->sy,
sizeof *gd->linedata); sizeof *gd->linedata);
@@ -496,7 +489,6 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
/* Move the history offset down over the line. */ /* Move the history offset down over the line. */
gd->hscrolled++; gd->hscrolled++;
gd->hsize++; gd->hsize++;
gd->scroll_added++;
} }
/* Expand line to fit to cell. */ /* Expand line to fit to cell. */
@@ -1518,7 +1510,6 @@ grid_reflow(struct grid *gd, u_int sx)
free(gd->linedata); free(gd->linedata);
gd->linedata = target->linedata; gd->linedata = target->linedata;
free(target); free(target);
gd->scroll_generation++;
} }
/* Convert to position based on wrapped lines. */ /* Convert to position based on wrapped lines. */

View File

@@ -450,11 +450,11 @@ sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
new->set_ra = si->set_ra; new->set_ra = si->set_ra;
/* subtract offset */ /* subtract offset */
new->ra_x = si->ra_x > pox ? si->ra_x - pox : 0; new->ra_x = new->ra_x > pox ? new->ra_x - pox : 0;
new->ra_y = si->ra_y > poy ? si->ra_y - poy : 0; new->ra_y = new->ra_y > poy ? new->ra_y - poy : 0;
/* clamp to size */ /* clamp to size */
new->ra_x = new->ra_x < psx ? new->ra_x : psx; new->ra_x = si->ra_x < psx ? si->ra_x : psx;
new->ra_y = new->ra_y < psy ? new->ra_y : psy; new->ra_y = si->ra_y < psy ? si->ra_y : psy;
/* resize */ /* resize */
new->ra_x = new->ra_x * xpixel / si->xpixel; new->ra_x = new->ra_x * xpixel / si->xpixel;
new->ra_y = new->ra_y * ypixel / si->ypixel; new->ra_y = new->ra_y * ypixel / si->ypixel;

View File

@@ -483,7 +483,7 @@ input_key_vt10x(struct bufferevent *bev, key_code key)
{ {
struct utf8_data ud; struct utf8_data ud;
key_code onlykey; key_code onlykey;
const char *p; char *p;
static const char *standard_map[2] = { static const char *standard_map[2] = {
"1!9(0)=+;:'\",<.>/-8? 2", "1!9(0)=+;:'\",<.>/-8? 2",
"119900=+;;'',,..\x1f\x1f\x7f\x7f\0\0", "119900=+;;'',,..\x1f\x1f\x7f\x7f\0\0",
@@ -806,7 +806,7 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m)
return; return;
if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
return; return;
if (!window_pane_is_visible(wp)) if (!window_pane_visible(wp))
return; return;
if (!input_key_get_mouse(s, m, x, y, &buf, &len)) if (!input_key_get_mouse(s, m, x, y, &buf, &len))
return; return;

18
input.c
View File

@@ -1365,7 +1365,7 @@ input_esc_dispatch(struct input_ctx *ictx)
{ {
struct screen_write_ctx *sctx = &ictx->ctx; struct screen_write_ctx *sctx = &ictx->ctx;
struct screen *s = sctx->s; struct screen *s = sctx->s;
const struct input_table_entry *entry; struct input_table_entry *entry;
if (ictx->flags & INPUT_DISCARD) if (ictx->flags & INPUT_DISCARD)
return (0); return (0);
@@ -1439,12 +1439,12 @@ input_esc_dispatch(struct input_ctx *ictx)
static int static int
input_csi_dispatch(struct input_ctx *ictx) input_csi_dispatch(struct input_ctx *ictx)
{ {
struct screen_write_ctx *sctx = &ictx->ctx; struct screen_write_ctx *sctx = &ictx->ctx;
struct screen *s = sctx->s; struct screen *s = sctx->s;
const struct input_table_entry *entry; struct input_table_entry *entry;
struct options *oo; struct options *oo;
int i, n, m, ek, set, p; int i, n, m, ek, set, p;
u_int cx, bg = ictx->cell.cell.bg; u_int cx, bg = ictx->cell.cell.bg;
if (ictx->flags & INPUT_DISCARD) if (ictx->flags & INPUT_DISCARD)
return (0); return (0);
@@ -3055,7 +3055,7 @@ input_osc_10(struct input_ctx *ictx, const char *p)
return; return;
c = window_pane_get_fg_control_client(wp); c = window_pane_get_fg_control_client(wp);
if (c == -1) { if (c == -1) {
tty_default_colours(&defaults, wp, NULL); tty_default_colours(&defaults, wp);
if (COLOUR_DEFAULT(defaults.fg)) if (COLOUR_DEFAULT(defaults.fg))
c = window_pane_get_fg(wp); c = window_pane_get_fg(wp);
else else
@@ -3225,7 +3225,7 @@ static int
input_osc_52_parse(struct input_ctx *ictx, const char *p, u_char **out, input_osc_52_parse(struct input_ctx *ictx, const char *p, u_char **out,
int *outlen, char *clip) int *outlen, char *clip)
{ {
const char *end; char *end;
size_t len; size_t len;
const char *allow = "cpqs01234567"; const char *allow = "cpqs01234567";
u_int i, j = 0; u_int i, j = 0;

View File

@@ -25,10 +25,11 @@
#include "tmux.h" #include "tmux.h"
#define DEFAULT_SESSION_MENU \ #define DEFAULT_SESSION_MENU \
" #{S/t:#{?#{&&:#{<:#{loop_index},6},#{!:#{session_active}}},'Switch To #[underscore]#{session_name}' '' {switch-client -t=#{session_id}#} ,}}" \ " 'Next' 'n' {switch-client -n}" \
" 'Previous' 'p' {switch-client -p}" \
" ''" \ " ''" \
" 'Renumber' 'N' {move-window -r}" \ " 'Renumber' 'N' {move-window -r}" \
" 'Rename' 'r' {command-prompt -I '#S' {rename-session -- '%%'}}" \ " 'Rename' 'r' {command-prompt -I \"#S\" {rename-session -- '%%'}}" \
" 'Detach' 'd' {detach-client}" \ " 'Detach' 'd' {detach-client}" \
" ''" \ " ''" \
" 'New Session' 's' {new-session}" \ " 'New Session' 's' {new-session}" \
@@ -64,7 +65,7 @@
" ''" \ " ''" \
" '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Up,}' 'u' {swap-pane -U}" \ " '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Up,}' 'u' {swap-pane -U}" \
" '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Down,}' 'd' {swap-pane -D}" \ " '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Down,}' 'd' {swap-pane -D}" \
" '#{?#{!:#{pane_floating_flag}},#{?pane_marked_set,,-}Swap Marked,}' 's' {swap-pane}" \ " '#{?pane_marked_set,,-}Swap Marked' 's' {swap-pane}" \
" ''" \ " ''" \
" 'Kill' 'X' {kill-pane}" \ " 'Kill' 'X' {kill-pane}" \
" 'Respawn' 'R' {respawn-pane -k}" \ " 'Respawn' 'R' {respawn-pane -k}" \
@@ -405,10 +406,8 @@ key_bindings_init(void)
"bind -N 'Choose a window from a list' w { choose-tree -Zw }", "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 '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 }", "bind -N 'Zoom the active pane' z { resize-pane -Z }",
"bind -N 'Move pane to top-left corner' '{' { resize-pane -x50% -y50%; move-pane -P top-left }", "bind -N 'Swap the active pane with the pane above' '{' { swap-pane -U }",
"bind -N 'Move pane to top-right corner' '}' { resize-pane -x50% -y50%; move-pane -P top-right }", "bind -N 'Swap the active pane with the pane below' '}' { swap-pane -D }",
"bind -N 'Move pane to bottom-left corner' 'M-{' { resize-pane -x50% -y50%; move-pane -P bottom-left }",
"bind -N 'Move pane to bottom-right corner' 'M-}' { resize-pane -x50% -y50%; move-pane -P bottom-right }",
"bind -N 'Show messages' '~' { show-messages }", "bind -N 'Show messages' '~' { show-messages }",
"bind -N 'Enter copy mode and scroll up' PPage { copy-mode -u }", "bind -N 'Enter copy mode and scroll up' PPage { copy-mode -u }",
"bind -N 'Select the pane above the active pane' -r Up { select-pane -U }", "bind -N 'Select the pane above the active pane' -r Up { select-pane -U }",
@@ -430,13 +429,13 @@ key_bindings_init(void)
"bind -N 'Move the visible part of the window left' -r S-Left { refresh-client -L 10 }", "bind -N 'Move the visible part of the window left' -r S-Left { refresh-client -L 10 }",
"bind -N 'Move the visible part of the window right' -r S-Right { refresh-client -R 10 }", "bind -N 'Move the visible part of the window right' -r S-Right { refresh-client -R 10 }",
"bind -N 'Reset so the visible part of the window follows the cursor' -r DC { refresh-client -c }", "bind -N 'Reset so the visible part of the window follows the cursor' -r DC { refresh-client -c }",
"bind -N 'Resize the pane up by 5' -r M-Up if -F '#{?floating_pane_flag}' { resizep -D-5 } { resize-pane -U 5 }", "bind -N 'Resize the pane up by 5' -r M-Up { resize-pane -U 5 }",
"bind -N 'Resize the pane down by 5' -r M-Down { resize-pane -D 5 }", "bind -N 'Resize the pane down by 5' -r M-Down { resize-pane -D 5 }",
"bind -N 'Resize the pane left by 5' -r M-Left if -F '#{?floating_pane_flag}' { resizep -R-5 } { resize-pane -L 5 }", "bind -N 'Resize the pane left by 5' -r M-Left { resize-pane -L 5 }",
"bind -N 'Resize the pane right by 5' -r M-Right resize-pane -R 5", "bind -N 'Resize the pane right by 5' -r M-Right { resize-pane -R 5 }",
"bind -N 'Resize the pane up' -r C-Up if -F '#{?floating_pane_flag}' { resizep -D-1 } { resize-pane -U }", "bind -N 'Resize the pane up' -r C-Up { resize-pane -U }",
"bind -N 'Resize the pane down' -r C-Down { resize-pane -D }", "bind -N 'Resize the pane down' -r C-Down { resize-pane -D }",
"bind -N 'Resize the pane left' -r C-Left if -F '#{?floating_pane_flag}' { resizep -R-1 } { resize-pane -L }", "bind -N 'Resize the pane left' -r C-Left { resize-pane -L }",
"bind -N 'Resize the pane right' -r C-Right { resize-pane -R }", "bind -N 'Resize the pane right' -r C-Right { resize-pane -R }",
/* Menu keys */ /* Menu keys */
@@ -449,7 +448,6 @@ key_bindings_init(void)
/* Mouse button 1 drag on pane. */ /* Mouse button 1 drag on pane. */
"bind -n MouseDrag1Pane { if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M } }", "bind -n MouseDrag1Pane { if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M } }",
"bind -n M-MouseDrag1Pane { move-pane -M }",
/* Mouse wheel up on pane. */ /* Mouse wheel up on pane. */
"bind -n WheelUpPane { if -F '#{||:#{alternate_on},#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -e } }", "bind -n WheelUpPane { if -F '#{||:#{alternate_on},#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -e } }",
@@ -468,7 +466,6 @@ key_bindings_init(void)
/* Mouse button 1 drag on border. */ /* Mouse button 1 drag on border. */
"bind -n MouseDrag1Border { resize-pane -M }", "bind -n MouseDrag1Border { resize-pane -M }",
"bind -n M-MouseDrag1Border { move-pane -M }",
/* Mouse button 1 down on status line. */ /* Mouse button 1 down on status line. */
"bind -n MouseDown1Status { switch-client -t= }", "bind -n MouseDown1Status { switch-client -t= }",
@@ -485,8 +482,8 @@ key_bindings_init(void)
"bind -n WheelUpStatus { previous-window }", "bind -n WheelUpStatus { previous-window }",
/* Mouse button 3 down on status left. */ /* Mouse button 3 down on status left. */
"bind -n MouseDown3StatusLeft { run -C \"display-menu -t= -xM -yW -T '#[align=centre]#{session_name}' " DEFAULT_SESSION_MENU "\" }", "bind -n MouseDown3StatusLeft { display-menu -t= -xM -yW -T '#[align=centre]#{session_name}' " DEFAULT_SESSION_MENU " }",
"bind -n M-MouseDown3StatusLeft { run -C \"display-menu -t= -xM -yW -T '#[align=centre]#{session_name}' " DEFAULT_SESSION_MENU "\" }", "bind -n M-MouseDown3StatusLeft { display-menu -t= -xM -yW -T '#[align=centre]#{session_name}' " DEFAULT_SESSION_MENU " }",
/* Mouse button 3 down on status line. */ /* Mouse button 3 down on status line. */
"bind -n MouseDown3Status { display-menu -t= -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU "}", "bind -n MouseDown3Status { display-menu -t= -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU "}",
@@ -514,8 +511,8 @@ key_bindings_init(void)
"bind -Tcopy-mode M-l { send -X cursor-centre-horizontal }", "bind -Tcopy-mode M-l { send -X cursor-centre-horizontal }",
"bind -Tcopy-mode C-n { send -X cursor-down }", "bind -Tcopy-mode C-n { send -X cursor-down }",
"bind -Tcopy-mode C-p { send -X cursor-up }", "bind -Tcopy-mode C-p { send -X cursor-up }",
"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-r { command-prompt -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-s { command-prompt -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-v { send -X page-down }",
"bind -Tcopy-mode C-w { send -X copy-pipe-and-cancel }", "bind -Tcopy-mode C-w { send -X copy-pipe-and-cancel }",
"bind -Tcopy-mode Escape { send -X cancel }", "bind -Tcopy-mode Escape { send -X cancel }",
@@ -523,18 +520,18 @@ key_bindings_init(void)
"bind -Tcopy-mode Space { send -X page-down }", "bind -Tcopy-mode Space { send -X page-down }",
"bind -Tcopy-mode , { send -X jump-reverse }", "bind -Tcopy-mode , { send -X jump-reverse }",
"bind -Tcopy-mode \\; { send -X jump-again }", "bind -Tcopy-mode \\; { send -X jump-again }",
"bind -Tcopy-mode F { command-prompt -P -1p'(jump backward)' { send -X jump-backward -- '%%' } }", "bind -Tcopy-mode F { command-prompt -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
"bind -Tcopy-mode N { send -X search-reverse }", "bind -Tcopy-mode N { send -X search-reverse }",
"bind -Tcopy-mode P { send -X toggle-position }", "bind -Tcopy-mode P { send -X toggle-position }",
"bind -Tcopy-mode R { send -X rectangle-toggle }", "bind -Tcopy-mode R { send -X rectangle-toggle }",
"bind -Tcopy-mode T { command-prompt -P -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }", "bind -Tcopy-mode T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
"bind -Tcopy-mode X { send -X set-mark }", "bind -Tcopy-mode X { send -X set-mark }",
"bind -Tcopy-mode f { command-prompt -P -1p'(jump forward)' { send -X jump-forward -- '%%' } }", "bind -Tcopy-mode f { command-prompt -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
"bind -Tcopy-mode g { command-prompt -P -p'(goto line)' { send -X goto-line -- '%%' } }", "bind -Tcopy-mode g { command-prompt -p'(goto line)' { send -X goto-line -- '%%' } }",
"bind -Tcopy-mode n { send -X search-again }", "bind -Tcopy-mode n { send -X search-again }",
"bind -Tcopy-mode q { send -X cancel }", "bind -Tcopy-mode q { send -X cancel }",
"bind -Tcopy-mode r { send -X refresh-toggle }", "bind -Tcopy-mode r { send -X refresh-from-pane }",
"bind -Tcopy-mode t { command-prompt -P -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }", "bind -Tcopy-mode t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
"bind -Tcopy-mode Home { send -X start-of-line }", "bind -Tcopy-mode Home { send -X start-of-line }",
"bind -Tcopy-mode End { send -X end-of-line }", "bind -Tcopy-mode End { send -X end-of-line }",
"bind -Tcopy-mode MouseDown1Pane select-pane", "bind -Tcopy-mode MouseDown1Pane select-pane",
@@ -550,15 +547,15 @@ key_bindings_init(void)
"bind -Tcopy-mode Down { send -X cursor-down }", "bind -Tcopy-mode Down { send -X cursor-down }",
"bind -Tcopy-mode Left { send -X cursor-left }", "bind -Tcopy-mode Left { send -X cursor-left }",
"bind -Tcopy-mode Right { send -X cursor-right }", "bind -Tcopy-mode Right { send -X cursor-right }",
"bind -Tcopy-mode M-1 { command-prompt -P -Np'(repeat)' -I1 { send -N '%%' } }", "bind -Tcopy-mode M-1 { command-prompt -Np'(repeat)' -I1 { send -N '%%' } }",
"bind -Tcopy-mode M-2 { command-prompt -P -Np'(repeat)' -I2 { send -N '%%' } }", "bind -Tcopy-mode M-2 { command-prompt -Np'(repeat)' -I2 { send -N '%%' } }",
"bind -Tcopy-mode M-3 { command-prompt -P -Np'(repeat)' -I3 { send -N '%%' } }", "bind -Tcopy-mode M-3 { command-prompt -Np'(repeat)' -I3 { send -N '%%' } }",
"bind -Tcopy-mode M-4 { command-prompt -P -Np'(repeat)' -I4 { send -N '%%' } }", "bind -Tcopy-mode M-4 { command-prompt -Np'(repeat)' -I4 { send -N '%%' } }",
"bind -Tcopy-mode M-5 { command-prompt -P -Np'(repeat)' -I5 { send -N '%%' } }", "bind -Tcopy-mode M-5 { command-prompt -Np'(repeat)' -I5 { send -N '%%' } }",
"bind -Tcopy-mode M-6 { command-prompt -P -Np'(repeat)' -I6 { send -N '%%' } }", "bind -Tcopy-mode M-6 { command-prompt -Np'(repeat)' -I6 { send -N '%%' } }",
"bind -Tcopy-mode M-7 { command-prompt -P -Np'(repeat)' -I7 { send -N '%%' } }", "bind -Tcopy-mode M-7 { command-prompt -Np'(repeat)' -I7 { send -N '%%' } }",
"bind -Tcopy-mode M-8 { command-prompt -P -Np'(repeat)' -I8 { send -N '%%' } }", "bind -Tcopy-mode M-8 { command-prompt -Np'(repeat)' -I8 { send -N '%%' } }",
"bind -Tcopy-mode M-9 { command-prompt -P -Np'(repeat)' -I9 { send -N '%%' } }", "bind -Tcopy-mode M-9 { command-prompt -Np'(repeat)' -I9 { send -N '%%' } }",
"bind -Tcopy-mode M-< { send -X history-top }", "bind -Tcopy-mode M-< { send -X history-top }",
"bind -Tcopy-mode M-> { send -X history-bottom }", "bind -Tcopy-mode M-> { send -X history-bottom }",
"bind -Tcopy-mode M-R { send -X top-line }", "bind -Tcopy-mode M-R { send -X top-line }",
@@ -577,8 +574,6 @@ key_bindings_init(void)
"bind -Tcopy-mode M-Down { send -X halfpage-down }", "bind -Tcopy-mode M-Down { send -X halfpage-down }",
"bind -Tcopy-mode C-Up { send -X scroll-up }", "bind -Tcopy-mode C-Up { send -X scroll-up }",
"bind -Tcopy-mode C-Down { send -X scroll-down }", "bind -Tcopy-mode C-Down { send -X scroll-down }",
"bind -Tcopy-mode M-C-Up { send -X previous-prompt }",
"bind -Tcopy-mode M-C-Down { send -X next-prompt }",
/* Copy mode (vi) keys. */ /* Copy mode (vi) keys. */
"bind -Tcopy-mode-vi '#' { send -FX search-backward -- '#{copy_cursor_word}' }", "bind -Tcopy-mode-vi '#' { send -FX search-backward -- '#{copy_cursor_word}' }",
@@ -599,25 +594,25 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi Space { send -X begin-selection }", "bind -Tcopy-mode-vi Space { send -X begin-selection }",
"bind -Tcopy-mode-vi '$' { send -X end-of-line }", "bind -Tcopy-mode-vi '$' { send -X end-of-line }",
"bind -Tcopy-mode-vi , { send -X jump-reverse }", "bind -Tcopy-mode-vi , { send -X jump-reverse }",
"bind -Tcopy-mode-vi / { command-prompt -P -T search -p'(search down)' { send -X search-forward -- '%%' } }", "bind -Tcopy-mode-vi / { command-prompt -T search -p'(search down)' { send -X search-forward -- '%%' } }",
"bind -Tcopy-mode-vi 0 { send -X start-of-line }", "bind -Tcopy-mode-vi 0 { send -X start-of-line }",
"bind -Tcopy-mode-vi 1 { command-prompt -P -Np'(repeat)' -I1 { send -N '%%' } }", "bind -Tcopy-mode-vi 1 { command-prompt -Np'(repeat)' -I1 { send -N '%%' } }",
"bind -Tcopy-mode-vi 2 { command-prompt -P -Np'(repeat)' -I2 { send -N '%%' } }", "bind -Tcopy-mode-vi 2 { command-prompt -Np'(repeat)' -I2 { send -N '%%' } }",
"bind -Tcopy-mode-vi 3 { command-prompt -P -Np'(repeat)' -I3 { send -N '%%' } }", "bind -Tcopy-mode-vi 3 { command-prompt -Np'(repeat)' -I3 { send -N '%%' } }",
"bind -Tcopy-mode-vi 4 { command-prompt -P -Np'(repeat)' -I4 { send -N '%%' } }", "bind -Tcopy-mode-vi 4 { command-prompt -Np'(repeat)' -I4 { send -N '%%' } }",
"bind -Tcopy-mode-vi 5 { command-prompt -P -Np'(repeat)' -I5 { send -N '%%' } }", "bind -Tcopy-mode-vi 5 { command-prompt -Np'(repeat)' -I5 { send -N '%%' } }",
"bind -Tcopy-mode-vi 6 { command-prompt -P -Np'(repeat)' -I6 { send -N '%%' } }", "bind -Tcopy-mode-vi 6 { command-prompt -Np'(repeat)' -I6 { send -N '%%' } }",
"bind -Tcopy-mode-vi 7 { command-prompt -P -Np'(repeat)' -I7 { send -N '%%' } }", "bind -Tcopy-mode-vi 7 { command-prompt -Np'(repeat)' -I7 { send -N '%%' } }",
"bind -Tcopy-mode-vi 8 { command-prompt -P -Np'(repeat)' -I8 { send -N '%%' } }", "bind -Tcopy-mode-vi 8 { command-prompt -Np'(repeat)' -I8 { send -N '%%' } }",
"bind -Tcopy-mode-vi 9 { command-prompt -P -Np'(repeat)' -I9 { send -N '%%' } }", "bind -Tcopy-mode-vi 9 { command-prompt -Np'(repeat)' -I9 { send -N '%%' } }",
"bind -Tcopy-mode-vi : { command-prompt -P -p'(goto line)' { send -X goto-line -- '%%' } }", "bind -Tcopy-mode-vi : { command-prompt -p'(goto line)' { send -X goto-line -- '%%' } }",
"bind -Tcopy-mode-vi \\; { send -X jump-again }", "bind -Tcopy-mode-vi \\; { send -X jump-again }",
"bind -Tcopy-mode-vi ? { command-prompt -P -T search -p'(search up)' { send -X search-backward -- '%%' } }", "bind -Tcopy-mode-vi ? { command-prompt -T search -p'(search up)' { send -X search-backward -- '%%' } }",
"bind -Tcopy-mode-vi A { send -X append-selection-and-cancel }", "bind -Tcopy-mode-vi A { send -X append-selection-and-cancel }",
"bind -Tcopy-mode-vi B { send -X previous-space }", "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 D { send -X copy-pipe-end-of-line-and-cancel }",
"bind -Tcopy-mode-vi E { send -X next-space-end }", "bind -Tcopy-mode-vi E { send -X next-space-end }",
"bind -Tcopy-mode-vi F { command-prompt -P -1p'(jump backward)' { send -X jump-backward -- '%%' } }", "bind -Tcopy-mode-vi F { command-prompt -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
"bind -Tcopy-mode-vi G { send -X history-bottom }", "bind -Tcopy-mode-vi G { send -X history-bottom }",
"bind -Tcopy-mode-vi H { send -X top-line }", "bind -Tcopy-mode-vi H { send -X top-line }",
"bind -Tcopy-mode-vi J { send -X scroll-down }", "bind -Tcopy-mode-vi J { send -X scroll-down }",
@@ -626,14 +621,14 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi M { send -X middle-line }", "bind -Tcopy-mode-vi M { send -X middle-line }",
"bind -Tcopy-mode-vi N { send -X search-reverse }", "bind -Tcopy-mode-vi N { send -X search-reverse }",
"bind -Tcopy-mode-vi P { send -X toggle-position }", "bind -Tcopy-mode-vi P { send -X toggle-position }",
"bind -Tcopy-mode-vi T { command-prompt -P -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }", "bind -Tcopy-mode-vi T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
"bind -Tcopy-mode-vi V { send -X select-line }", "bind -Tcopy-mode-vi V { send -X select-line }",
"bind -Tcopy-mode-vi W { send -X next-space }", "bind -Tcopy-mode-vi W { send -X next-space }",
"bind -Tcopy-mode-vi X { send -X set-mark }", "bind -Tcopy-mode-vi X { send -X set-mark }",
"bind -Tcopy-mode-vi ^ { send -X back-to-indentation }", "bind -Tcopy-mode-vi ^ { send -X back-to-indentation }",
"bind -Tcopy-mode-vi b { send -X previous-word }", "bind -Tcopy-mode-vi b { send -X previous-word }",
"bind -Tcopy-mode-vi e { send -X next-word-end }", "bind -Tcopy-mode-vi e { send -X next-word-end }",
"bind -Tcopy-mode-vi f { command-prompt -P -1p'(jump forward)' { send -X jump-forward -- '%%' } }", "bind -Tcopy-mode-vi f { command-prompt -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
"bind -Tcopy-mode-vi g { send -X history-top }", "bind -Tcopy-mode-vi g { send -X history-top }",
"bind -Tcopy-mode-vi h { send -X cursor-left }", "bind -Tcopy-mode-vi h { send -X cursor-left }",
"bind -Tcopy-mode-vi j { send -X cursor-down }", "bind -Tcopy-mode-vi j { send -X cursor-down }",
@@ -643,8 +638,8 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi n { send -X search-again }", "bind -Tcopy-mode-vi n { send -X search-again }",
"bind -Tcopy-mode-vi o { send -X other-end }", "bind -Tcopy-mode-vi o { send -X other-end }",
"bind -Tcopy-mode-vi q { send -X cancel }", "bind -Tcopy-mode-vi q { send -X cancel }",
"bind -Tcopy-mode-vi r { send -X refresh-toggle }", "bind -Tcopy-mode-vi r { send -X refresh-from-pane }",
"bind -Tcopy-mode-vi t { command-prompt -P -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }", "bind -Tcopy-mode-vi t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
"bind -Tcopy-mode-vi v { send -X rectangle-toggle }", "bind -Tcopy-mode-vi v { send -X rectangle-toggle }",
"bind -Tcopy-mode-vi w { send -X next-word }", "bind -Tcopy-mode-vi w { send -X next-word }",
"bind -Tcopy-mode-vi '{' { send -X previous-paragraph }", "bind -Tcopy-mode-vi '{' { send -X previous-paragraph }",

View File

@@ -379,8 +379,6 @@ layout_construct(struct layout_cell *lcparent, const char **layout,
struct layout_cell *lcchild; struct layout_cell *lcchild;
*lc = layout_construct_cell(lcparent, layout); *lc = layout_construct_cell(lcparent, layout);
if (*lc == NULL)
return (-1);
switch (**layout) { switch (**layout) {
case ',': case ',':

407
layout.c
View File

@@ -20,25 +20,17 @@
#include <sys/types.h> #include <sys/types.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "tmux.h" #include "tmux.h"
/* /*
* The window layout is a tree of cells each of which can be one of: a * The window layout is a tree of cells each of which can be one of: a
* left-right container for a list of cells, a top-bottom container for a list * left-right container for a list of cells, a top-bottom container for a list
* of cells, or a container for a window pane. 'Node' will be used to refer to * of cells, or a container for a window pane.
* a cell which contains a list of cells, and 'leaf' to refer to a cell that
* contains a window pane. A leaf is considered to be 'tiled' if it is to be
* drawn as a part of the tiled layout. A 'neighbour' is a sibling that is also
* tiled. A cell's 'split' size refers to the side that is shortened when
* splitting it, determined by the parent's type.
* *
* Each window has a pointer to the root of its layout tree (containing its * Each window has a pointer to the root of its layout tree (containing its
* panes), every pane has a pointer back to the cell containing it, and each * panes), every pane has a pointer back to the cell containing it, and each
* cell a pointer to its parent cell. Every cell has a position in the root * cell a pointer to its parent cell.
* layout tree. This position is retained through cell state changes such as
* floating or hiding.
*/ */
static u_int layout_resize_check(struct window *, struct layout_cell *, static u_int layout_resize_check(struct window *, struct layout_cell *,
@@ -248,49 +240,6 @@ layout_fix_zindexes(struct window *w, struct layout_cell *lc)
} }
} }
static int
layout_cell_is_tiled(struct layout_cell *lc)
{
int is_leaf = lc->type == LAYOUT_WINDOWPANE;
int is_floating = lc->flags & LAYOUT_CELL_FLOATING;
return is_leaf && !is_floating;
}
static int
layout_cell_has_tiled_child(struct layout_cell *lc)
{
struct layout_cell *lcchild;
if (lc->type == LAYOUT_WINDOWPANE)
return (0);
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (layout_cell_is_tiled(lcchild) ||
layout_cell_has_tiled_child(lcchild))
return (1);
}
return (0);
}
static int
layout_cell_is_first_tiled(struct layout_cell *lc)
{
struct layout_cell *lcchild, *lcparent = lc->parent;
if (lcparent == NULL)
return (layout_cell_is_tiled(lc));
TAILQ_FOREACH(lcchild, &lcparent->cells, entry) {
if (layout_cell_is_tiled(lcchild) ||
layout_cell_has_tiled_child(lcchild))
break;
}
return (lcchild == lc);
}
/* Fix cell offsets for a child cell. */ /* Fix cell offsets for a child cell. */
static void static void
layout_fix_offsets1(struct layout_cell *lc) layout_fix_offsets1(struct layout_cell *lc)
@@ -301,8 +250,7 @@ layout_fix_offsets1(struct layout_cell *lc)
if (lc->type == LAYOUT_LEFTRIGHT) { if (lc->type == LAYOUT_LEFTRIGHT) {
xoff = lc->xoff; xoff = lc->xoff;
TAILQ_FOREACH(lcchild, &lc->cells, entry) { TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (!layout_cell_is_tiled(lcchild) && if (lcchild->flags & LAYOUT_CELL_FLOATING)
!layout_cell_has_tiled_child(lcchild))
continue; continue;
lcchild->xoff = xoff; lcchild->xoff = xoff;
lcchild->yoff = lc->yoff; lcchild->yoff = lc->yoff;
@@ -313,8 +261,7 @@ layout_fix_offsets1(struct layout_cell *lc)
} else { } else {
yoff = lc->yoff; yoff = lc->yoff;
TAILQ_FOREACH(lcchild, &lc->cells, entry) { TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (!layout_cell_is_tiled(lcchild) && if (lcchild->flags & LAYOUT_CELL_FLOATING)
!layout_cell_has_tiled_child(lcchild))
continue; continue;
lcchild->xoff = lc->xoff; lcchild->xoff = lc->xoff;
lcchild->yoff = yoff; lcchild->yoff = yoff;
@@ -345,15 +292,22 @@ layout_fix_offsets(struct window *w)
static int static int
layout_cell_is_top(struct window *w, struct layout_cell *lc) layout_cell_is_top(struct window *w, struct layout_cell *lc)
{ {
struct layout_cell *next; struct layout_cell *next, *edge;
while (lc != w->layout_root) { while (lc != w->layout_root) {
next = lc->parent; next = lc->parent;
if (next == NULL) if (next == NULL)
return (0); return (0);
if (next->type == LAYOUT_TOPBOTTOM && if (next->type == LAYOUT_TOPBOTTOM) {
!layout_cell_is_first_tiled(lc)) edge = TAILQ_FIRST(&next->cells);
return (0); while (edge != NULL) {
if (~edge->flags & LAYOUT_CELL_FLOATING)
break;
edge = TAILQ_NEXT(edge, entry);
}
if (lc != edge)
return (0);
}
lc = next; lc = next;
} }
return (1); return (1);
@@ -406,10 +360,9 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
struct window_pane *wp; struct window_pane *wp;
struct layout_cell *lc; struct layout_cell *lc;
int status, scrollbars, sb_pos, sb_w, sb_pad; int status, scrollbars, sb_pos, sb_w, sb_pad;
int old_xoff, old_yoff, changed = 0; u_int sx, sy;
u_int sx, sy, old_sx, old_sy;
status = window_get_pane_status(w); status = options_get_number(w->options, "pane-border-status");
scrollbars = options_get_number(w->options, "pane-scrollbars"); scrollbars = options_get_number(w->options, "pane-scrollbars");
sb_pos = options_get_number(w->options, "pane-scrollbars-position"); sb_pos = options_get_number(w->options, "pane-scrollbars-position");
@@ -417,11 +370,6 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
if ((lc = wp->layout_cell) == NULL || wp == skip) if ((lc = wp->layout_cell) == NULL || wp == skip)
continue; continue;
old_xoff = wp->xoff;
old_yoff = wp->yoff;
old_sx = wp->sx;
old_sy = wp->sy;
wp->xoff = lc->xoff; wp->xoff = lc->xoff;
wp->yoff = lc->yoff; wp->yoff = lc->yoff;
sx = lc->sx; sx = lc->sx;
@@ -442,7 +390,7 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
if (sb_pad < 0) if (sb_pad < 0)
sb_pad = 0; sb_pad = 0;
if (sb_pos == PANE_SCROLLBARS_LEFT) { if (sb_pos == PANE_SCROLLBARS_LEFT) {
if ((int)sx - sb_w - sb_pad < PANE_MINIMUM) { if ((int)sx - sb_w < PANE_MINIMUM) {
wp->xoff = wp->xoff + wp->xoff = wp->xoff +
(int)sx - PANE_MINIMUM; (int)sx - PANE_MINIMUM;
sx = PANE_MINIMUM; sx = PANE_MINIMUM;
@@ -459,15 +407,7 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
} }
window_pane_resize(wp, sx, sy); 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. */ /* Count the number of available cells in a layout. */
@@ -500,7 +440,7 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
u_int available, minimum; u_int available, minimum;
int status, scrollbars; int status, scrollbars;
status = window_get_pane_status(w); status = options_get_number(w->options, "pane-border-status");
scrollbars = options_get_number(w->options, "pane-scrollbars"); scrollbars = options_get_number(w->options, "pane-scrollbars");
if (lc->type == LAYOUT_WINDOWPANE) { if (lc->type == LAYOUT_WINDOWPANE) {
@@ -565,20 +505,13 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc,
/* Child cell runs in a different direction. */ /* Child cell runs in a different direction. */
if (lc->type != type) { if (lc->type != type) {
TAILQ_FOREACH(lcchild, &lc->cells, entry) { TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (!layout_cell_is_tiled(lcchild) && if (lcchild->flags & LAYOUT_CELL_FLOATING)
!layout_cell_has_tiled_child(lcchild))
continue; continue;
layout_resize_adjust(w, lcchild, type, change); layout_resize_adjust(w, lcchild, type, change);
} }
return; return;
} }
/*
* If a node doesn't contain any tiled cells, there is nothing to do.
*/
if (!layout_cell_has_tiled_child(lc))
return;
/* /*
* Child cell runs in the same direction. Adjust each child equally * Child cell runs in the same direction. Adjust each child equally
* until no further change is possible. * until no further change is possible.
@@ -587,8 +520,7 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc,
TAILQ_FOREACH(lcchild, &lc->cells, entry) { TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (change == 0) if (change == 0)
break; break;
if (!layout_cell_is_tiled(lcchild) && if (lcchild->flags & LAYOUT_CELL_FLOATING)
!layout_cell_has_tiled_child(lcchild))
continue; continue;
if (change > 0) { if (change > 0) {
layout_resize_adjust(w, lcchild, type, 1); layout_resize_adjust(w, lcchild, type, 1);
@@ -603,57 +535,12 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc,
} }
} }
/* Find and return the nearest neighbour to a cell in a specific direction. */
static struct layout_cell *
layout_cell_get_neighbour_direction(struct layout_cell *lc, int direction)
{
struct layout_cell *lcn = lc;
while (1) {
if (direction)
lcn = TAILQ_NEXT(lcn, entry);
else
lcn = TAILQ_PREV(lcn, layout_cells, entry);
if (lcn == NULL ||
layout_cell_is_tiled(lcn) ||
layout_cell_has_tiled_child(lcn))
return (lcn);
}
}
/*
* Find and return the nearest neighbour. Prefers cells "after" the specified
* cell. This behavior defines how cell dimensions are redistributed when a cell
* is hidden/shown and floated/tiled.
*/
struct layout_cell *
layout_cell_get_neighbour(struct layout_cell *lc)
{
struct layout_cell *lcother, *lcparent = lc->parent;
int direction = 1;
if (lcparent == NULL)
return (NULL);
if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
direction = !direction;
lcother = layout_cell_get_neighbour_direction(lc, direction);
if (lcother == NULL)
lcother = layout_cell_get_neighbour_direction(lc, !direction);
return (lcother);
}
/* Destroy a cell and redistribute the space. */ /* Destroy a cell and redistribute the space. */
void void
layout_destroy_cell(struct window *w, struct layout_cell *lc, layout_destroy_cell(struct window *w, struct layout_cell *lc,
struct layout_cell **lcroot) struct layout_cell **lcroot)
{ {
struct layout_cell *lcother = NULL, *lcparent; struct layout_cell *lcother = NULL, *lcparent;
int change;
/* If no parent, this is the last pane in a window. */ /* If no parent, this is the last pane in a window. */
lcparent = lc->parent; lcparent = lc->parent;
@@ -664,27 +551,27 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc,
return; return;
} }
if (!layout_cell_is_tiled(lc)) { if (~lc->flags & LAYOUT_CELL_FLOATING) {
TAILQ_REMOVE(&lcparent->cells, lc, entry); /* Merge the space into the previous or next cell. */
layout_free_cell(lc); if (lc == TAILQ_FIRST(&lcparent->cells))
goto out; lcother = TAILQ_NEXT(lc, entry);
}
lcother = layout_cell_get_neighbour(lc);
if (lcother != NULL) {
if (lcparent->type == LAYOUT_LEFTRIGHT)
change = lc->sx + 1;
else else
change = lc->sy + 1; lcother = TAILQ_PREV(lc, layout_cells, entry);
layout_resize_adjust(w, lcother, lcparent->type, change); }
} else if (lcother != NULL && (~lcother->flags & LAYOUT_CELL_FLOATING)) {
layout_remove_tile(w, lcparent); if (lcparent->type == LAYOUT_LEFTRIGHT) {
layout_resize_adjust(w, lcother, lcparent->type,
lc->sx + 1);
} else {
layout_resize_adjust(w, lcother, lcparent->type,
lc->sy + 1);
}
}
/* Remove this from the parent's list. */ /* Remove this from the parent's list. */
TAILQ_REMOVE(&lcparent->cells, lc, entry); TAILQ_REMOVE(&lcparent->cells, lc, entry);
layout_free_cell(lc); layout_free_cell(lc);
out:
/* /*
* If the parent now has one cell, remove the parent from the tree and * If the parent now has one cell, remove the parent from the tree and
* replace it by that cell. * replace it by that cell.
@@ -695,7 +582,7 @@ out:
lc->parent = lcparent->parent; lc->parent = lcparent->parent;
if (lc->parent == NULL) { if (lc->parent == NULL) {
if (layout_cell_is_tiled(lc)) { if (~lc->flags & LAYOUT_CELL_FLOATING) {
lc->xoff = 0; lc->xoff = 0;
lc->yoff = 0; lc->yoff = 0;
} }
@@ -813,76 +700,6 @@ layout_resize_pane_to(struct window_pane *wp, enum layout_type type,
layout_resize_pane(wp, type, change, 1); layout_resize_pane(wp, type, change, 1);
} }
/* Resize a floating pane to an absolute size. */
void
layout_resize_floating_pane_to(struct window_pane *wp, enum layout_type type,
u_int size, char **cause)
{
struct layout_cell *lc = wp->layout_cell;
if (~lc->flags & LAYOUT_CELL_FLOATING) {
*cause = xstrdup("pane is not floating");
return;
}
if (window_pane_get_pane_lines(wp) != PANE_LINES_NONE &&
size >= PANE_MINIMUM + 2)
size -= 2;
if (size < PANE_MINIMUM || size > PANE_MAXIMUM) {
*cause = xstrdup("size is too big or too small");
return;
}
if (type == LAYOUT_TOPBOTTOM) {
if (lc->sy == size)
return;
lc->sy = size;
} else {
if (lc->sx == size)
return;
lc->sx = size;
}
redraw_invalidate_scene(wp->window);
}
/* Resize a floating pane relative to its current size. */
void
layout_resize_floating_pane(struct window_pane *wp, enum layout_type type,
int change, int opposite, char **cause)
{
struct layout_cell *lc = wp->layout_cell;
u_int size;
if (~lc->flags & LAYOUT_CELL_FLOATING) {
*cause = xstrdup("pane is not floating");
return;
}
if (change == 0)
return;
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;
}
lc->sy = size;
if (opposite)
lc->yoff -= change;
} else {
size = lc->sx + change;
if (size < PANE_MINIMUM || size > PANE_MAXIMUM) {
*cause = xstrdup("change is too big or too small");
return;
}
lc->sx = size;
if (opposite)
lc->xoff -= change;
}
redraw_invalidate_scene(wp->window);
}
/* Resize a layout cell. */
void void
layout_resize_layout(struct window *w, struct layout_cell *lc, layout_resize_layout(struct window *w, struct layout_cell *lc,
enum layout_type type, int change, int opposite) enum layout_type type, int change, int opposite)
@@ -916,7 +733,9 @@ void
layout_resize_pane(struct window_pane *wp, enum layout_type type, int change, layout_resize_pane(struct window_pane *wp, enum layout_type type, int change,
int opposite) int opposite)
{ {
struct layout_cell *lc = wp->layout_cell, *lcparent; struct layout_cell *lc, *lcparent;
lc = wp->layout_cell;
/* Find next parent of the same type. */ /* Find next parent of the same type. */
lcparent = lc->parent; lcparent = lc->parent;
@@ -928,11 +747,8 @@ layout_resize_pane(struct window_pane *wp, enum layout_type type, int change,
return; return;
/* If this is the last cell, move back one. */ /* If this is the last cell, move back one. */
if (lc == TAILQ_LAST(&lcparent->cells, layout_cells)) { if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
do lc = TAILQ_PREV(lc, layout_cells, entry);
lc = TAILQ_PREV(lc, layout_cells, entry);
while (lc->flags & LAYOUT_CELL_FLOATING);
}
layout_resize_layout(wp->window, lc, type, change, opposite); layout_resize_layout(wp->window, lc, type, change, opposite);
} }
@@ -1131,8 +947,7 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc)
count = 0; count = 0;
previous = 0; previous = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry) { TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (!layout_cell_is_tiled(lcchild) && if (lcchild->flags & LAYOUT_CELL_FLOATING)
!layout_cell_has_tiled_child(lcchild))
continue; continue;
count++; count++;
if (lc->type == LAYOUT_LEFTRIGHT) if (lc->type == LAYOUT_LEFTRIGHT)
@@ -1152,8 +967,7 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc)
/* Resize children into the new size. */ /* Resize children into the new size. */
idx = 0; idx = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry) { TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (!layout_cell_is_tiled(lcchild) && if (lcchild->flags & LAYOUT_CELL_FLOATING)
!layout_cell_has_tiled_child(lcchild))
continue; continue;
if (lc->type == LAYOUT_TOPBOTTOM) { if (lc->type == LAYOUT_TOPBOTTOM) {
lcchild->sx = lc->sx; lcchild->sx = lc->sx;
@@ -1199,7 +1013,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
lc = wp->window->layout_root; lc = wp->window->layout_root;
else else
lc = wp->layout_cell; lc = wp->layout_cell;
status = window_get_pane_status(wp->window); status = options_get_number(wp->window->options, "pane-border-status");
scrollbars = options_get_number(wp->window->options, "pane-scrollbars"); scrollbars = options_get_number(wp->window->options, "pane-scrollbars");
/* Copy the old cell size. */ /* Copy the old cell size. */
@@ -1360,21 +1174,14 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
* layout_assign_pane before much else happens! * layout_assign_pane before much else happens!
*/ */
struct layout_cell * struct layout_cell *
layout_floating_pane(struct window *w, struct window_pane *wp, u_int sx, layout_floating_pane(struct window *w, u_int sx, u_int sy, int ox, int oy)
u_int sy, int ox, int oy)
{ {
struct layout_cell *lc, *lcnew, *lcparent; struct layout_cell *lc = w->layout_root, *lcnew, *lcparent;
if (wp == NULL) if (lc->type == LAYOUT_WINDOWPANE) {
lc = w->layout_root;
else
lc = wp->layout_cell;
lcparent = lc->parent;
if (lcparent == NULL) {
/* /*
* Adding a pane to a root that isn't node. Must create and * Adding a pane to a root that doesn't have a container. Must
* insert a new root. * create and insert a new root.
*/ */
lcparent = layout_create_cell(NULL); lcparent = layout_create_cell(NULL);
layout_make_node(lcparent, LAYOUT_TOPBOTTOM); layout_make_node(lcparent, LAYOUT_TOPBOTTOM);
@@ -1384,10 +1191,11 @@ layout_floating_pane(struct window *w, struct window_pane *wp, u_int sx,
/* Insert the old cell. */ /* Insert the old cell. */
lc->parent = lcparent; lc->parent = lcparent;
TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry); TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
} } else
lcparent = w->layout_root;
lcnew = layout_create_cell(lcparent); lcnew = layout_create_cell(lcparent);
TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry); TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry);
lcnew->flags |= LAYOUT_CELL_FLOATING; lcnew->flags |= LAYOUT_CELL_FLOATING;
layout_set_size(lcnew, sx, sy, ox, oy); layout_set_size(lcnew, sx, sy, ox, oy);
@@ -1428,7 +1236,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
number++; number++;
if (number <= 1) if (number <= 1)
return (0); return (0);
status = window_get_pane_status(w); status = options_get_number(w->options, "pane-border-status");
if (parent->type == LAYOUT_LEFTRIGHT) if (parent->type == LAYOUT_LEFTRIGHT)
size = parent->sx; size = parent->sx;
@@ -1508,7 +1316,6 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
enum layout_type type; enum layout_type type;
u_int curval; u_int curval;
int size = -1; int size = -1;
char *error = NULL;
if (window_pane_is_floating(wp)) { if (window_pane_is_floating(wp)) {
*cause = xstrdup("can't split a floating pane"); *cause = xstrdup("can't split a floating pane");
@@ -1535,16 +1342,15 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
if (args_has(args, 'l')) { if (args_has(args, 'l')) {
size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval, size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
item, &error); item, cause);
} else if (args_has(args, 'p')) { } else if (args_has(args, 'p')) {
size = args_strtonum_and_expand(args, 'p', 0, 100, item, size = args_strtonum_and_expand(args, 'p', 0, 100, item,
&error); cause);
if (error == NULL) if (*cause == NULL)
size = curval * size / 100; size = curval * size / 100;
} }
if (error != NULL) { if (*cause != NULL) {
xasprintf(cause, "invalid tiled geometry %s", error); *cause = xstrdup("invalid tiled geometry");
free(error);
return (NULL); return (NULL);
} }
@@ -1564,53 +1370,35 @@ layout_get_tiled_cell(struct cmdq_item *item, struct args *args,
/* Get a new floating cell. */ /* Get a new floating cell. */
struct layout_cell * struct layout_cell *
layout_get_floating_cell(struct cmdq_item *item, struct args *args, layout_get_floating_cell(struct cmdq_item *item, struct args *args,
enum pane_lines lines, struct window *w, struct window_pane *wp, struct window *w, __unused struct window_pane *wp, char **cause)
char **cause)
{ {
struct layout_cell *lcnew; struct layout_cell *lcnew;
int sx = w->sx / 2, sy = w->sy / 4; int sx = w->sx / 2, sy = w->sy / 4;
int ox = INT_MAX, oy = INT_MAX; int ox = INT_MAX, oy = INT_MAX;
char *error;
if (args_has(args, 'x')) { if (args_has(args, 'x')) {
sx = args_percentage_and_expand(args, 'x', 0, PANE_MAXIMUM, sx = args_percentage_and_expand(args, 'x', 0, w->sx - 1, w->sx,
w->sx, item, &error); item, cause);
if (error != NULL) { if (*cause != NULL)
xasprintf(cause, "position %s", error);
free(error);
return (NULL); return (NULL);
}
if (lines != PANE_LINES_NONE)
sx -= 2;
} }
if (args_has(args, 'y')) { if (args_has(args, 'y')) {
sy = args_percentage_and_expand(args, 'y', 0, PANE_MAXIMUM, sy = args_percentage_and_expand(args, 'y', 0, w->sy - 1, w->sy,
w->sy, item, &error); item, cause);
if (error != NULL) { if (*cause != NULL)
xasprintf(cause, "position %s", error);
free(error);
return (NULL); return (NULL);
}
if (lines != PANE_LINES_NONE)
sy -= 2;
} }
if (args_has(args, 'X')) { if (args_has(args, 'X')) {
ox = args_percentage_and_expand(args, 'X', -sx, w->sx, ox = args_percentage_and_expand(args, 'X', -sx, w->sx,
w->sx, item, &error); w->sx, item, cause);
if (error != NULL) { if (*cause != NULL)
xasprintf(cause, "size %s", error);
free(error);
return (NULL); return (NULL);
}
} }
if (args_has(args, 'Y')) { if (args_has(args, 'Y')) {
oy = args_percentage_and_expand(args, 'Y', -sy, w->sy, oy = args_percentage_and_expand(args, 'Y', -sy, w->sy,
w->sy, item, &error); w->sy, item, cause);
if (error != NULL) { if (*cause != NULL)
xasprintf(cause, "size %s", error);
free(error);
return (NULL); return (NULL);
}
} }
if (ox == INT_MAX) { if (ox == INT_MAX) {
@@ -1622,9 +1410,7 @@ layout_get_floating_cell(struct cmdq_item *item, struct args *args,
ox = 4; ox = 4;
} }
w->last_new_pane_x = ox; w->last_new_pane_x = ox;
} else }
if (lines != PANE_LINES_NONE)
ox += 1;
if (oy == INT_MAX) { if (oy == INT_MAX) {
if (w->last_new_pane_y == 0) if (w->last_new_pane_y == 0)
oy = 2; oy = 2;
@@ -1634,55 +1420,8 @@ layout_get_floating_cell(struct cmdq_item *item, struct args *args,
oy = 2; oy = 2;
} }
w->last_new_pane_y = oy; 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 (NULL);
}
if (sy < PANE_MINIMUM || sy > PANE_MAXIMUM) {
*cause = xstrdup("invalid height");
return (NULL);
} }
lcnew = layout_floating_pane(w, wp, sx, sy, ox, oy); lcnew = layout_floating_pane(w, sx, sy, ox, oy);
return (lcnew); return (lcnew);
} }
/*
* Removes a cell from the tiled layout by giving the cell's space to the
* nearest neighbour.
*/
int
layout_remove_tile(struct window *w, struct layout_cell *lc)
{
struct layout_cell *lcneighbour, *lcparent;
enum layout_type type;
int change;
if (lc->flags & LAYOUT_CELL_FLOATING)
return (0);
lcneighbour = layout_cell_get_neighbour(lc);
if (lcneighbour == NULL) {
if (lc->parent != NULL)
layout_remove_tile(w, lc->parent);
} else if ((lcparent = lcneighbour->parent) != NULL) {
type = lcparent->type;
/*
* Adding the size of the layout cell plus its border to the
* neighbour.
*/
if (type == LAYOUT_TOPBOTTOM)
change = lc->sy + 1;
else
change = lc->sx + 1;
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);
return (1);
}

15
menu.c
View File

@@ -257,7 +257,8 @@ menu_reapply_styles(struct menu_data *md, struct client *c)
} }
void void
menu_draw_cb(struct client *c, void *data) menu_draw_cb(struct client *c, void *data,
__unused struct screen_redraw_ctx *rctx)
{ {
struct menu_data *md = data; struct menu_data *md = data;
struct tty *tty = &c->tty; struct tty *tty = &c->tty;
@@ -280,8 +281,10 @@ menu_draw_cb(struct client *c, void *data)
&md->style_gc, &md->border_style_gc, &md->selected_style_gc); &md->style_gc, &md->border_style_gc, &md->selected_style_gc);
screen_write_stop(&ctx); screen_write_stop(&ctx);
for (i = 0; i < screen_size_y(&md->s); i++) for (i = 0; i < screen_size_y(&md->s); i++) {
tty_draw_line(tty, s, 0, i, menu->width + 4, px, py + i, NULL); tty_draw_line(tty, s, 0, i, menu->width + 4, px, py + i,
&grid_default_cell, NULL);
}
} }
void void
@@ -437,7 +440,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
break; break;
} }
} }
while ((name == NULL || *name == '-') && md->choice != 0) { while (name == NULL || *name == '-') {
md->choice--; md->choice--;
name = menu->items[md->choice].name; name = menu->items[md->choice].name;
} }
@@ -447,7 +450,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
case KEYC_HOME: case KEYC_HOME:
md->choice = 0; md->choice = 0;
name = menu->items[md->choice].name; name = menu->items[md->choice].name;
while ((name == NULL || *name == '-') && md->choice != count - 1) { while (name == NULL || *name == '-') {
md->choice++; md->choice++;
name = menu->items[md->choice].name; name = menu->items[md->choice].name;
} }
@@ -457,7 +460,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
case KEYC_END: case KEYC_END:
md->choice = count - 1; md->choice = count - 1;
name = menu->items[md->choice].name; name = menu->items[md->choice].name;
while ((name == NULL || *name == '-') && md->choice != 0) { while (name == NULL || *name == '-') {
md->choice--; md->choice--;
name = menu->items[md->choice].name; name = menu->items[md->choice].name;
} }

View File

@@ -37,7 +37,6 @@ enum mode_tree_preview {
}; };
struct mode_tree_item; struct mode_tree_item;
struct mode_tree_prompt;
TAILQ_HEAD(mode_tree_list, mode_tree_item); TAILQ_HEAD(mode_tree_list, mode_tree_item);
struct mode_tree_data { struct mode_tree_data {
@@ -50,13 +49,12 @@ struct mode_tree_data {
const struct menu_item *menu; const struct menu_item *menu;
struct sort_criteria sort_crit; struct sort_criteria sort_crit;
const char *view_name;
mode_tree_build_cb buildcb; mode_tree_build_cb buildcb;
mode_tree_draw_cb drawcb; mode_tree_draw_cb drawcb;
mode_tree_search_cb searchcb; mode_tree_search_cb searchcb;
mode_tree_menu_cb menucb; mode_tree_menu_cb menucb;
mode_tree_height_cb heightcb; mode_tree_height_cb heightcb;
mode_tree_key_cb keycb; mode_tree_key_cb keycb;
mode_tree_swap_cb swapcb; mode_tree_swap_cb swapcb;
mode_tree_sort_cb sortcb; mode_tree_sort_cb sortcb;
@@ -78,10 +76,6 @@ struct mode_tree_data {
u_int current; u_int current;
struct screen screen; struct screen screen;
struct prompt *prompt;
struct mode_tree_prompt *prompt_data;
u_int prompt_cx;
int prompt_top;
int preview; int preview;
char *search; char *search;
@@ -89,7 +83,6 @@ struct mode_tree_data {
int no_matches; int no_matches;
enum mode_tree_search_dir search_dir; enum mode_tree_search_dir search_dir;
int search_icase; int search_icase;
int help;
}; };
struct mode_tree_item { struct mode_tree_item {
@@ -129,25 +122,7 @@ struct mode_tree_menu {
u_int line; u_int line;
}; };
/* static void mode_tree_free_items(struct mode_tree_list *);
* 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[] = { static const struct menu_item mode_tree_menu_items[] = {
{ "Scroll Left", '<', NULL }, { "Scroll Left", '<', NULL },
@@ -159,31 +134,31 @@ static const struct menu_item mode_tree_menu_items[] = {
}; };
static const char* mode_tree_help_start[] = { static const char* mode_tree_help_start[] = {
"#[bold] Up, k #[default]#[acs]x#[default] Move cursor up", "\r\033[1m Up, k \033[0m\016x\017 \033[0mMove cursor up\n",
"#[bold] Down, j #[default]#[acs]x#[default] Move cursor down", "\r\033[1m Down, j \033[0m\016x\017 \033[0mMove cursor down\n",
"#[bold] g #[default]#[acs]x#[default] Go to top", "\r\033[1m g \033[0m\016x\017 \033[0mGo to top\n",
"#[bold] G #[default]#[acs]x#[default] Go to bottom", "\r\033[1m G \033[0m\016x\017 \033[0mGo to bottom\n",
"#[bold] PPage, C-b #[default]#[acs]x#[default] Page up", "\r\033[1m PPage, C-b \033[0m\016x\017 \033[0mPage up\n",
"#[bold] NPage, C-f #[default]#[acs]x#[default] Page down", "\r\033[1m NPage, C-f \033[0m\016x\017 \033[0mPage down\n",
"#[bold] Left, h #[default]#[acs]x#[default] Collapse %1", "\r\033[1m Left, h \033[0m\016x\017 \033[0mCollapse %1\n",
"#[bold] Right, l #[default]#[acs]x#[default] Expand %1", "\r\033[1m Right, l \033[0m\016x\017 \033[0mExpand %1\n",
"#[bold] M-- #[default]#[acs]x#[default] Collapse all %1s", "\r\033[1m M-- \033[0m\016x\017 \033[0mCollapse all %1s\n",
"#[bold] M-+ #[default]#[acs]x#[default] Expand all %1s", "\r\033[1m M-+ \033[0m\016x\017 \033[0mExpand all %1s\n",
"#[bold] t #[default]#[acs]x#[default] Toggle %1 tag", "\r\033[1m t \033[0m\016x\017 \033[0mToggle %1 tag\n",
"#[bold] T #[default]#[acs]x#[default] Untag all %1s", "\r\033[1m T \033[0m\016x\017 \033[0mUntag all %1s\n",
"#[bold] C-t #[default]#[acs]x#[default] Tag all %1s", "\r\033[1m C-t \033[0m\016x\017 \033[0mTag all %1s\n",
"#[bold] C-s #[default]#[acs]x#[default] Search forward", "\r\033[1m C-s \033[0m\016x\017 \033[0mSearch forward\n",
"#[bold] C-r #[default]#[acs]x#[default] Search backward", "\r\033[1m C-r \033[0m\016x\017 \033[0mSearch backward\n",
"#[bold] n #[default]#[acs]x#[default] Repeat search forward", "\r\033[1m n \033[0m\016x\017 \033[0mRepeat search forward\n",
"#[bold] N #[default]#[acs]x#[default] Repeat search backward", "\r\033[1m N \033[0m\016x\017 \033[0mRepeat search backward\n",
"#[bold] f #[default]#[acs]x#[default] Filter %1s", "\r\033[1m f \033[0m\016x\017 \033[0mFilter %1s\n",
"#[bold] O #[default]#[acs]x#[default] Change sort order", "\r\033[1m O \033[0m\016x\017 \033[0mChange sort order\n",
"#[bold] r #[default]#[acs]x#[default] Reverse sort order", "\r\033[1m r \033[0m\016x\017 \033[0mReverse sort order\n",
"#[bold] v #[default]#[acs]x#[default] Toggle preview", "\r\033[1m v \033[0m\016x\017 \033[0mToggle preview\n",
NULL NULL
}; };
static const char* mode_tree_help_end[] = { static const char* mode_tree_help_end[] = {
"#[bold] q, Escape #[default]#[acs]x#[default] Exit mode", "\r\033[1m q, Escape \033[0m\016x\017 \033[0mExit mode\033[H",
NULL NULL
}; };
#define MODE_TREE_HELP_DEFAULT_WIDTH 39 #define MODE_TREE_HELP_DEFAULT_WIDTH 39
@@ -403,13 +378,6 @@ mode_tree_get_current_name(struct mode_tree_data *mtd)
return (mtd->line_list[mtd->current].item->name); 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 void
mode_tree_expand_current(struct mode_tree_data *mtd) mode_tree_expand_current(struct mode_tree_data *mtd)
{ {
@@ -537,9 +505,7 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mtd->modedata = modedata; mtd->modedata = modedata;
mtd->menu = menu; mtd->menu = menu;
if (drawcb == NULL) if (args_has(args, 'N') > 1)
mtd->preview = MODE_TREE_PREVIEW_OFF;
else if (args_has(args, 'N') > 1)
mtd->preview = MODE_TREE_PREVIEW_BIG; mtd->preview = MODE_TREE_PREVIEW_BIG;
else if (args_has(args, 'N')) else if (args_has(args, 'N'))
mtd->preview = MODE_TREE_PREVIEW_OFF; mtd->preview = MODE_TREE_PREVIEW_OFF;
@@ -671,7 +637,6 @@ mode_tree_free(struct mode_tree_data *mtd)
if (mtd->zoomed == 0) if (mtd->zoomed == 0)
server_unzoom_window(wp->window); server_unzoom_window(wp->window);
mode_tree_clear_prompt(mtd);
mode_tree_free_items(&mtd->children); mode_tree_free_items(&mtd->children);
mode_tree_clear_lines(mtd); mode_tree_clear_lines(mtd);
screen_free(&mtd->screen); screen_free(&mtd->screen);
@@ -735,12 +700,6 @@ mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
return (mti); return (mti);
} }
void
mode_tree_view_name(struct mode_tree_data *mtd, const char *name)
{
mtd->view_name = name;
}
void void
mode_tree_draw_as_parent(struct mode_tree_item *mti) mode_tree_draw_as_parent(struct mode_tree_item *mti)
{ {
@@ -925,12 +884,9 @@ mode_tree_draw(struct mode_tree_data *mtd)
screen_write_box(&ctx, w, sy - h, BOX_LINES_DEFAULT, NULL, NULL); screen_write_box(&ctx, w, sy - h, BOX_LINES_DEFAULT, NULL, NULL);
if (mtd->sort_crit.order_seq != NULL) { if (mtd->sort_crit.order_seq != NULL) {
xasprintf(&text, " %s (sort: %s%s)%s%s%s", mti->name, xasprintf(&text, " %s (sort: %s%s)", mti->name,
sort_order_to_string(mtd->sort_crit.order), sort_order_to_string(mtd->sort_crit.order),
mtd->sort_crit.reversed ? ", reversed" : "", mtd->sort_crit.reversed ? ", reversed" : "");
mtd->view_name == NULL ? "" : " (view: ",
mtd->view_name == NULL ? "" : mtd->view_name,
mtd->view_name == NULL ? "" : ")");
} else } else
xasprintf(&text, " %s", mti->name); xasprintf(&text, " %s", mti->name);
if (w - 2 >= strlen(text)) { if (w - 2 >= strlen(text)) {
@@ -962,150 +918,10 @@ mode_tree_draw(struct mode_tree_data *mtd)
} }
done: done:
if (mtd->help) screen_write_cursormove(&ctx, 0, mtd->current - mtd->offset, 0);
mode_tree_draw_help(mtd, &ctx);
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); 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->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 * static struct mode_tree_item *
mode_tree_search_backward(struct mode_tree_data *mtd) mode_tree_search_backward(struct mode_tree_data *mtd)
{ {
@@ -1226,37 +1042,41 @@ mode_tree_search_set(struct mode_tree_data *mtd)
mtd->wp->flags |= PANE_REDRAW; mtd->wp->flags |= PANE_REDRAW;
} }
static enum prompt_result static int
mode_tree_search_callback(__unused struct client *c, void *data, const char *s, mode_tree_search_callback(__unused struct client *c, void *data, const char *s,
enum prompt_key_result key) __unused int done)
{ {
struct mode_tree_data *mtd = data; struct mode_tree_data *mtd = data;
if (mtd->dead) if (mtd->dead)
return (PROMPT_CLOSE); return (0);
free(mtd->search); free(mtd->search);
if (s == NULL || *s == '\0') if (s == NULL || *s == '\0') {
mtd->search = NULL; mtd->search = NULL;
else { return (0);
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);
if (key == PROMPT_KEY_HANDLED) return (0);
return (PROMPT_CONTINUE);
return (PROMPT_CLOSE);
} }
static enum prompt_result static void
mode_tree_search_free(void *data)
{
mode_tree_remove_ref(data);
}
static int
mode_tree_filter_callback(__unused struct client *c, void *data, const char *s, mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
enum prompt_key_result key) __unused int done)
{ {
struct mode_tree_data *mtd = data; struct mode_tree_data *mtd = data;
if (mtd->dead) if (mtd->dead)
return (PROMPT_CLOSE); return (0);
if (mtd->filter != NULL) if (mtd->filter != NULL)
free(mtd->filter); free(mtd->filter);
@@ -1269,20 +1089,13 @@ mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
mode_tree_draw(mtd); mode_tree_draw(mtd);
mtd->wp->flags |= PANE_REDRAW; mtd->wp->flags |= PANE_REDRAW;
if (key == PROMPT_KEY_HANDLED) return (0);
return (PROMPT_CONTINUE);
return (PROMPT_CLOSE);
} }
static void static void
mode_tree_clear_filter(struct mode_tree_data *mtd) mode_tree_filter_free(void *data)
{ {
free(mtd->filter); mode_tree_remove_ref(data);
mtd->filter = NULL;
mode_tree_build(mtd);
mode_tree_draw(mtd);
mtd->wp->flags |= PANE_REDRAW;
} }
static void static void
@@ -1352,28 +1165,12 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
} }
static void static void
mode_tree_draw_help_line(struct screen_write_ctx *ctx, mode_tree_display_help(__unused struct mode_tree_data *mtd, struct client *c)
const struct grid_cell *gc, const char *line, const char *item, u_int x,
u_int y, u_int w)
{ {
char *expanded; struct session *s = c->session;
u_int px, py, w, h = 0;
expanded = cmd_template_replace(line, item, 1); const char **line, **lines = NULL, *item = "item";
screen_write_cursormove(ctx, x, y, 0); char *new_line;
screen_write_clearcharacter(ctx, w, gc->bg);
screen_write_cursormove(ctx, x, y, 0);
format_draw(ctx, gc, w, expanded, NULL, 0);
free(expanded);
}
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;
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;
if (mtd->helpcb == NULL) if (mtd->helpcb == NULL)
w = MODE_TREE_HELP_DEFAULT_WIDTH; w = MODE_TREE_HELP_DEFAULT_WIDTH;
@@ -1389,32 +1186,33 @@ mode_tree_draw_help(struct mode_tree_data *mtd, struct screen_write_ctx *ctx)
for (line = mode_tree_help_end; *line != NULL; line++) for (line = mode_tree_help_end; *line != NULL; line++)
h++; h++;
box_w = w + 2; if (c->tty.sx < w || c->tty.sy < h)
box_h = h + 2;
if (sx < box_w || sy < box_h)
return; return;
x = (sx - box_w) / 2; px = (c->tty.sx - w) / 2;
y = (sy - box_h) / 2; py = (c->tty.sy - h) / 2;
memcpy(&gc, &grid_default_cell, sizeof gc); if (popup_display(POPUP_CLOSEANYKEY|POPUP_NOJOB, BOX_LINES_DEFAULT,
screen_write_cursormove(ctx, x, y, 0); NULL, px, py, w, h, NULL, NULL, 0, NULL, NULL, NULL, c, s, NULL,
screen_write_box(ctx, box_w, box_h, BOX_LINES_DEFAULT, &gc, NULL); NULL, NULL, NULL) != 0)
return;
y++; popup_write(c, "\033[H\033[?25l\033[?7l\033)0", 17);
x++; for (line = mode_tree_help_start; *line != NULL; line++) {
for (line = mode_tree_help_start; *line != NULL; line++, y++) new_line = cmd_template_replace(*line, item, 1);
mode_tree_draw_help_line(ctx, &gc, *line, item, x, y, w); popup_write(c, new_line, strlen(new_line));
for (line = lines; line != NULL && *line != NULL; line++, y++) free(new_line);
mode_tree_draw_help_line(ctx, &gc, *line, item, x, y, w); }
for (line = mode_tree_help_end; *line != NULL; line++, y++) for (line = lines; line != NULL && *line != NULL; line++) {
mode_tree_draw_help_line(ctx, &gc, *line, item, x, y, w); new_line = cmd_template_replace(*line, item, 1);
} popup_write(c, new_line, strlen(new_line));
free(new_line);
static void }
mode_tree_display_help(struct mode_tree_data *mtd) for (line = mode_tree_help_end; *line != NULL; line++) {
{ new_line = cmd_template_replace(*line, item, 1);
mtd->help = 1; popup_write(c, new_line, strlen(new_line));
mode_tree_draw(mtd); free(new_line);
}
popup_write(c, "\033[H", 3);
} }
int int
@@ -1423,77 +1221,14 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
{ {
struct mode_tree_line *line; struct mode_tree_line *line;
struct mode_tree_item *current, *parent, *mti; struct mode_tree_item *current, *parent, *mti;
u_int i, x, y, py, sx; u_int i, x, y;
int choice, preview; int choice, preview;
enum prompt_key_result result;
int redraw;
struct prompt *prompt;
struct mode_tree_prompt *mtp;
if (mtd->line_size == 0) { if (mtd->line_size == 0) {
*key = KEYC_NONE; *key = KEYC_NONE;
return (1); 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);
}
mtd->help = 0;
mode_tree_draw(mtd);
*key = KEYC_NONE;
return (0);
}
if (KEYC_IS_MOUSE(*key) && m != NULL) { if (KEYC_IS_MOUSE(*key) && m != NULL) {
if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) { if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
*key = KEYC_NONE; *key = KEYC_NONE;
@@ -1559,7 +1294,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
return (1); return (1);
case KEYC_F1: case KEYC_F1:
case 'h'|KEYC_CTRL: case 'h'|KEYC_CTRL:
mode_tree_display_help(mtd); mode_tree_display_help(mtd, c);
break; break;
case KEYC_UP: case KEYC_UP:
case 'k': case 'k':
@@ -1689,10 +1424,11 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
case '?': case '?':
case '/': case '/':
case 's'|KEYC_CTRL: case 's'|KEYC_CTRL:
mtd->references++;
mtd->search_dir = MODE_TREE_SEARCH_FORWARD; mtd->search_dir = MODE_TREE_SEARCH_FORWARD;
mode_tree_set_prompt(mtd, c, "(search) ", "", status_prompt_set(c, NULL, "(search) ", "",
PROMPT_TYPE_SEARCH, PROMPT_NOFORMAT, mode_tree_search_callback, mode_tree_search_free, mtd,
mode_tree_search_callback, NULL, mtd); PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH);
break; break;
case 'n': case 'n':
mtd->search_dir = MODE_TREE_SEARCH_FORWARD; mtd->search_dir = MODE_TREE_SEARCH_FORWARD;
@@ -1703,13 +1439,10 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
mode_tree_search_set(mtd); mode_tree_search_set(mtd);
break; break;
case 'f': case 'f':
mode_tree_set_prompt(mtd, c, "(filter) ", mtd->filter, mtd->references++;
PROMPT_TYPE_SEARCH, PROMPT_NOFORMAT, status_prompt_set(c, NULL, "(filter) ", mtd->filter,
mode_tree_filter_callback, NULL, mtd); mode_tree_filter_callback, mode_tree_filter_free, mtd,
break; PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH);
case 'c':
mode_tree_clear_prompt(mtd);
mode_tree_clear_filter(mtd);
break; break;
case 'v': case 'v':
switch (mtd->preview) { switch (mtd->preview) {

View File

@@ -70,13 +70,13 @@ static const char *options_table_pane_scrollbars_position_list[] = {
"right", "left", NULL "right", "left", NULL
}; };
static const char *options_table_pane_status_list[] = { static const char *options_table_pane_status_list[] = {
"off", "top", "bottom", "top-floating", "bottom-floating", NULL "off", "top", "bottom", NULL
}; };
static const char *options_table_pane_border_indicators_list[] = { static const char *options_table_pane_border_indicators_list[] = {
"off", "colour", "arrows", "both", NULL "off", "colour", "arrows", "both", NULL
}; };
static const char *options_table_pane_border_lines_list[] = { static const char *options_table_pane_border_lines_list[] = {
"single", "double", "heavy", "simple", "number", "spaces", "none", NULL "single", "double", "heavy", "simple", "number", "spaces", NULL
}; };
static const char *options_table_popup_border_lines_list[] = { static const char *options_table_popup_border_lines_list[] = {
"single", "double", "heavy", "simple", "rounded", "padded", "none", NULL "single", "double", "heavy", "simple", "rounded", "padded", "none", NULL
@@ -327,10 +327,9 @@ const struct options_table_entry options_table[] = {
}, },
{ .name = "cursor-colour", { .name = "cursor-colour",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.flags = OPTIONS_TABLE_IS_COLOUR, .default_num = -1,
.default_str = "",
.text = "Colour of the cursor." .text = "Colour of the cursor."
}, },
@@ -635,29 +634,19 @@ const struct options_table_entry options_table[] = {
}, },
{ .name = "display-panes-active-colour", { .name = "display-panes-active-colour",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
.flags = OPTIONS_TABLE_IS_COLOUR, .default_num = 1,
.default_str = "red",
.text = "Colour of the active pane for 'display-panes'." .text = "Colour of the active pane for 'display-panes'."
}, },
{ .name = "display-panes-colour", { .name = "display-panes-colour",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
.flags = OPTIONS_TABLE_IS_COLOUR, .default_num = 4,
.default_str = "blue",
.text = "Colour of not active panes for 'display-panes'." .text = "Colour of not active panes for 'display-panes'."
}, },
{ .name = "display-panes-format",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "#[align=right]#{pane_width}x#{pane_height}",
.text = "Format of text shown by 'display-panes', expanded for each "
"pane."
},
{ .name = "display-panes-time", { .name = "display-panes-time",
.type = OPTIONS_TABLE_NUMBER, .type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
@@ -741,7 +730,7 @@ const struct options_table_entry options_table[] = {
.flags = OPTIONS_TABLE_IS_STYLE, .flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",", .separator = ",",
.text = "Style of the command prompt when in command mode, if " .text = "Style of the command prompt when in command mode, if "
"'status-keys' is set to 'vi'." "'mode-keys' is set to 'vi'."
}, },
{ .name = "message-format", { .name = "message-format",
@@ -766,9 +755,7 @@ const struct options_table_entry options_table[] = {
{ .name = "message-style", { .name = "message-style",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
.default_str = "bg=yellow,fg=black," .default_str = "bg=yellow,fg=black,fill=yellow",
"#{?#{m/r:(^|#,)IS(PANE|MODE)($|#,),#{prompt_flags}},,"
"fill=yellow}",
.flags = OPTIONS_TABLE_IS_STYLE, .flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",", .separator = ",",
.text = "Style of messages and the command prompt. " .text = "Style of messages and the command prompt. "
@@ -993,10 +980,9 @@ const struct options_table_entry options_table[] = {
}, },
{ .name = "prompt-cursor-colour", { .name = "prompt-cursor-colour",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
.flags = OPTIONS_TABLE_IS_COLOUR, .default_num = -1,
.default_str = "",
.text = "Colour of the cursor when in the command prompt." .text = "Colour of the cursor when in the command prompt."
}, },
@@ -1148,10 +1134,9 @@ const struct options_table_entry options_table[] = {
}, },
{ .name = "clock-mode-colour", { .name = "clock-mode-colour",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_COLOUR,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW,
.flags = OPTIONS_TABLE_IS_COLOUR, .default_num = 4,
.default_str = "blue",
.text = "Colour of the clock in clock mode." .text = "Colour of the clock in clock mode."
}, },
@@ -1371,7 +1356,7 @@ const struct options_table_entry options_table[] = {
{ .name = "pane-border-lines", { .name = "pane-border-lines",
.type = OPTIONS_TABLE_CHOICE, .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_pane_border_lines_list, .choices = options_table_pane_border_lines_list,
.default_num = PANE_LINES_SINGLE, .default_num = PANE_LINES_SINGLE,
.text = "Type of characters used to draw pane border lines. Some of " .text = "Type of characters used to draw pane border lines. Some of "
@@ -1380,7 +1365,7 @@ const struct options_table_entry options_table[] = {
{ .name = "pane-border-status", { .name = "pane-border-status",
.type = OPTIONS_TABLE_CHOICE, .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_pane_status_list, .choices = options_table_pane_status_list,
.default_num = PANE_STATUS_OFF, .default_num = PANE_STATUS_OFF,
.text = "Position of the pane status lines." .text = "Position of the pane status lines."

View File

@@ -662,18 +662,6 @@ options_parse_get(struct options *oo, const char *s, int *idx, int only)
return (o); 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 * char *
options_match(const char *s, int *idx, int *ambiguous) options_match(const char *s, int *idx, int *ambiguous)
{ {
@@ -989,12 +977,9 @@ struct style *
options_string_to_style(struct options *oo, const char *name, options_string_to_style(struct options *oo, const char *name,
struct format_tree *ft) struct format_tree *ft)
{ {
struct options_entry *o; struct options_entry *o;
const struct options_table_entry *oe; const char *s;
const struct grid_cell *dgc = &grid_default_cell; char *expanded;
const char *s;
char *expanded;
int failed;
o = options_get(oo, name); o = options_get(oo, name);
if (o == NULL || !OPTIONS_IS_STRING(o)) if (o == NULL || !OPTIONS_IS_STRING(o))
@@ -1003,27 +988,20 @@ options_string_to_style(struct options *oo, const char *name,
if (o->cached) if (o->cached)
return (&o->style); return (&o->style);
s = o->value.string; s = o->value.string;
oe = o->tableentry;
log_debug("%s: %s is '%s'", __func__, name, s); log_debug("%s: %s is '%s'", __func__, name, s);
style_set(&o->style, dgc); style_set(&o->style, &grid_default_cell);
o->cached = (strstr(s, "#{") == NULL); o->cached = (strstr(s, "#{") == NULL);
if (ft != NULL && !o->cached) { if (ft != NULL && !o->cached) {
expanded = format_expand(ft, s); expanded = format_expand(ft, s);
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_COLOUR)) if (style_parse(&o->style, &grid_default_cell, expanded) != 0) {
failed = style_parse_colour(&o->style, dgc, expanded); free(expanded);
else
failed = style_parse(&o->style, dgc, expanded);
free(expanded);
if (failed != 0)
return (NULL); return (NULL);
}
free(expanded);
} else { } else {
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_COLOUR)) if (style_parse(&o->style, &grid_default_cell, s) != 0)
failed = style_parse_colour(&o->style, dgc, s);
else
failed = style_parse(&o->style, dgc, s);
if (failed != 0)
return (NULL); return (NULL);
} }
return (&o->style); return (&o->style);
@@ -1051,12 +1029,6 @@ options_from_string_check(const struct options_table_entry *oe,
xasprintf(cause, "invalid style: %s", value); xasprintf(cause, "invalid style: %s", value);
return (-1); 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); return (0);
} }
@@ -1247,15 +1219,6 @@ options_push_changes(const char *name)
if (strcmp(name, "status") == 0 || if (strcmp(name, "status") == 0 ||
strcmp(name, "status-interval") == 0) strcmp(name, "status-interval") == 0)
status_timer_start_all(); 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-position") == 0 ||
strcmp(name, "pane-scrollbars-style") == 0)
redraw_invalidate_all_scenes();
if (strcmp(name, "monitor-silence") == 0) if (strcmp(name, "monitor-silence") == 0)
alerts_reset_all(); alerts_reset_all();
if (strcmp(name, "window-style") == 0 || if (strcmp(name, "window-style") == 0 ||

169
popup.c
View File

@@ -75,6 +75,12 @@ struct popup_data {
u_int lb; u_int lb;
}; };
struct popup_editor {
char *path;
popup_finish_edit_cb cb;
void *arg;
};
static const struct menu_item popup_menu_items[] = { static const struct menu_item popup_menu_items[] = {
{ "Close", 'q', NULL }, { "Close", 'q', NULL },
{ "#{?buffer_name,Paste #[underscore]#{buffer_name},}", 'p', NULL }, { "#{?buffer_name,Paste #[underscore]#{buffer_name},}", 'p', NULL },
@@ -88,6 +94,15 @@ static const struct menu_item popup_menu_items[] = {
{ NULL, KEYC_NONE, NULL } { NULL, KEYC_NONE, NULL }
}; };
static const struct menu_item popup_internal_menu_items[] = {
{ "Close", 'q', NULL },
{ "", KEYC_NONE, NULL },
{ "Fill Space", 'F', NULL },
{ "Centre", 'C', NULL },
{ NULL, KEYC_NONE, NULL }
};
static void static void
popup_free(struct popup_data *pd) popup_free(struct popup_data *pd)
{ {
@@ -193,8 +208,7 @@ popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
memcpy(&ttyctx->defaults, &pd->defaults, sizeof ttyctx->defaults); memcpy(&ttyctx->defaults, &pd->defaults, sizeof ttyctx->defaults);
ttyctx->flags &= ~TTY_CTX_WINDOW_BIGGER; ttyctx->flags &= ~TTY_CTX_WINDOW_BIGGER;
ttyctx->style_ctx.defaults = &ttyctx->defaults; ttyctx->palette = &pd->palette;
ttyctx->style_ctx.palette = &pd->palette;
ttyctx->redraw_cb = popup_redraw_cb; ttyctx->redraw_cb = popup_redraw_cb;
ttyctx->set_client_cb = popup_set_client_cb; ttyctx->set_client_cb = popup_set_client_cb;
ttyctx->arg = pd; ttyctx->arg = pd;
@@ -273,15 +287,15 @@ popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx)
} }
static void static void
popup_draw_cb(struct client *c, void *data) popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx)
{ {
struct popup_data *pd = data; struct popup_data *pd = data;
struct tty *tty = &c->tty; struct tty *tty = &c->tty;
struct screen s; struct screen s;
struct screen_write_ctx ctx; struct screen_write_ctx ctx;
u_int i, px = pd->px, py = pd->py; u_int i, px = pd->px, py = pd->py;
struct colour_palette *palette = &pd->palette;
struct grid_cell defaults; struct grid_cell defaults;
struct tty_style_ctx style_ctx;
popup_reapply_styles(pd); popup_reapply_styles(pd);
@@ -307,13 +321,9 @@ popup_draw_cb(struct client *c, void *data)
memcpy(&defaults, &pd->defaults, sizeof defaults); memcpy(&defaults, &pd->defaults, sizeof defaults);
if (defaults.fg == 8) if (defaults.fg == 8)
defaults.fg = pd->palette.fg; defaults.fg = palette->fg;
if (defaults.bg == 8) if (defaults.bg == 8)
defaults.bg = pd->palette.bg; defaults.bg = palette->bg;
style_ctx.defaults = &defaults;
style_ctx.palette = &pd->palette;
style_ctx.dim = 0;
style_ctx.hyperlinks = s.hyperlinks;
if (pd->md != NULL) { if (pd->md != NULL) {
c->overlay_check = menu_check_cb; c->overlay_check = menu_check_cb;
@@ -322,13 +332,15 @@ popup_draw_cb(struct client *c, void *data)
c->overlay_check = NULL; c->overlay_check = NULL;
c->overlay_data = NULL; c->overlay_data = NULL;
} }
for (i = 0; i < pd->sy; i++) for (i = 0; i < pd->sy; i++) {
tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &style_ctx); tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &defaults,
palette);
}
screen_free(&s); screen_free(&s);
if (pd->md != NULL) { if (pd->md != NULL) {
c->overlay_check = NULL; c->overlay_check = NULL;
c->overlay_data = NULL; c->overlay_data = NULL;
menu_draw_cb(c, pd->md); menu_draw_cb(c, pd->md, rctx);
} }
c->overlay_check = popup_check_cb; c->overlay_check = popup_check_cb;
c->overlay_data = pd; c->overlay_data = pd;
@@ -635,7 +647,11 @@ popup_key_cb(struct client *c, void *data, struct key_event *event)
menu: menu:
pd->menu = menu_create(""); pd->menu = menu_create("");
menu_add_items(pd->menu, popup_menu_items, NULL, c, NULL); if (pd->flags & POPUP_INTERNAL) {
menu_add_items(pd->menu, popup_internal_menu_items, NULL, c,
NULL);
} else
menu_add_items(pd->menu, popup_menu_items, NULL, c, NULL);
if (m->x >= (pd->menu->width + 4) / 2) if (m->x >= (pd->menu->width + 4) / 2)
x = m->x - (pd->menu->width + 4) / 2; x = m->x - (pd->menu->width + 4) / 2;
else else
@@ -842,16 +858,127 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
pd->psx = sx; pd->psx = sx;
pd->psy = sy; pd->psy = sy;
pd->job = job_run(shellcmd, argc, argv, env, s, cwd, if (flags & POPUP_NOJOB)
popup_job_update_cb, popup_job_complete_cb, NULL, pd, pd->ictx = input_init(NULL, NULL, &pd->palette, NULL);
JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy); else {
if (pd->job == NULL) { pd->job = job_run(shellcmd, argc, argv, env, s, cwd,
popup_free(pd); popup_job_update_cb, popup_job_complete_cb, NULL, pd,
return (-1); JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy);
if (pd->job == NULL) {
popup_free(pd);
return (-1);
}
pd->ictx = input_init(NULL, job_get_event(pd->job),
&pd->palette, c);
} }
pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette, c);
server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb, server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb,
popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd); popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd);
return (0); return (0);
} }
void
popup_write(struct client *c, const char *data, size_t size)
{
struct popup_data *pd = c->overlay_data;
if (!popup_present(c))
return;
c->overlay_check = NULL;
c->overlay_data = NULL;
input_parse_screen(pd->ictx, &pd->s, popup_init_ctx_cb, pd, data, size);
c->overlay_check = popup_check_cb;
c->overlay_data = pd;
}
static void
popup_editor_free(struct popup_editor *pe)
{
unlink(pe->path);
free(pe->path);
free(pe);
}
static void
popup_editor_close_cb(int status, void *arg)
{
struct popup_editor *pe = arg;
FILE *f;
char *buf = NULL;
off_t len = 0;
if (status != 0) {
pe->cb(NULL, 0, pe->arg);
popup_editor_free(pe);
return;
}
f = fopen(pe->path, "r");
if (f != NULL) {
fseeko(f, 0, SEEK_END);
len = ftello(f);
fseeko(f, 0, SEEK_SET);
if (len == 0 ||
(uintmax_t)len > (uintmax_t)SIZE_MAX ||
(buf = malloc(len)) == NULL ||
fread(buf, len, 1, f) != 1) {
free(buf);
buf = NULL;
len = 0;
}
fclose(f);
}
pe->cb(buf, len, pe->arg); /* callback now owns buffer */
popup_editor_free(pe);
}
int
popup_editor(struct client *c, const char *buf, size_t len,
popup_finish_edit_cb cb, void *arg)
{
struct popup_editor *pe;
int fd;
FILE *f;
char *cmd;
char path[] = _PATH_TMP "tmux.XXXXXXXX";
const char *editor;
u_int px, py, sx, sy;
editor = options_get_string(global_options, "editor");
if (*editor == '\0')
return (-1);
fd = mkstemp(path);
if (fd == -1)
return (-1);
f = fdopen(fd, "w");
if (f == NULL)
return (-1);
if (fwrite(buf, len, 1, f) != 1) {
fclose(f);
return (-1);
}
fclose(f);
pe = xcalloc(1, sizeof *pe);
pe->path = xstrdup(path);
pe->cb = cb;
pe->arg = arg;
sx = c->tty.sx * 9 / 10;
sy = c->tty.sy * 9 / 10;
px = (c->tty.sx / 2) - (sx / 2);
py = (c->tty.sy / 2) - (sy / 2);
xasprintf(&cmd, "%s %s", editor, path);
if (popup_display(POPUP_INTERNAL|POPUP_CLOSEEXIT, BOX_LINES_DEFAULT,
NULL, px, py, sx, sy, NULL, cmd, 0, NULL, _PATH_TMP, NULL, c, NULL,
NULL, NULL, popup_editor_close_cb, pe) != 0) {
popup_editor_free(pe);
free(cmd);
return (-1);
}
free(cmd);
return (0);
}

12
proc.c
View File

@@ -57,7 +57,6 @@ struct tmuxpeer {
struct imsgbuf ibuf; struct imsgbuf ibuf;
struct event event; struct event event;
uid_t uid; uid_t uid;
gid_t gid;
int flags; int flags;
#define PEER_BAD 0x1 #define PEER_BAD 0x1
@@ -302,6 +301,7 @@ proc_add_peer(struct tmuxproc *tp, int fd,
void (*dispatchcb)(struct imsg *, void *), void *arg) void (*dispatchcb)(struct imsg *, void *), void *arg)
{ {
struct tmuxpeer *peer; struct tmuxpeer *peer;
gid_t gid;
peer = xcalloc(1, sizeof *peer); peer = xcalloc(1, sizeof *peer);
peer->parent = tp; peer->parent = tp;
@@ -314,10 +314,8 @@ proc_add_peer(struct tmuxproc *tp, int fd,
imsgbuf_allow_fdpass(&peer->ibuf); imsgbuf_allow_fdpass(&peer->ibuf);
event_set(&peer->event, fd, EV_READ, proc_event_cb, peer); event_set(&peer->event, fd, EV_READ, proc_event_cb, peer);
if (getpeereid(fd, &peer->uid, &peer->gid) != 0) { if (getpeereid(fd, &peer->uid, &gid) != 0)
peer->uid = (uid_t)-1; peer->uid = (uid_t)-1;
peer->gid = (gid_t)-1;
}
log_debug("add peer %p: %d (%p)", peer, fd, arg); log_debug("add peer %p: %d (%p)", peer, fd, arg);
TAILQ_INSERT_TAIL(&tp->peers, peer, entry); TAILQ_INSERT_TAIL(&tp->peers, peer, entry);
@@ -386,9 +384,3 @@ proc_get_peer_uid(struct tmuxpeer *peer)
{ {
return (peer->uid); return (peer->uid);
} }
gid_t
proc_get_peer_gid(struct tmuxpeer *peer)
{
return (peer->gid);
}

View File

@@ -1,264 +0,0 @@
/* $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;
}

1597
prompt.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,140 +0,0 @@
#!/bin/sh
# Name validation policy.
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 "$*"
$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
$TMUX set-option -qg allow-set-title on || exit 1
$TMUX set-option -qg allow-rename on || exit 1
$TMUX set-option -qg automatic-rename off || exit 1
# Commands reject ':' and '.' for sessions and windows, but allow '#'.
$TMUX rename-session 'session#ok' || fail "session name with # rejected"
must_equal "$($TMUX display-message -p '#{session_name}')" 'session#ok'
must_fail $TMUX rename-session 'session:bad'
must_fail $TMUX rename-session 'session.bad'
$TMUX rename-window 'window#ok' || fail "window name with # rejected"
must_equal "$($TMUX display-message -p '#{window_name}')" 'window#ok'
must_fail $TMUX rename-window 'window:bad'
must_fail $TMUX rename-window 'window.bad'
$TMUX set-option -q @name 'format#ok' || exit 1
$TMUX rename-session '#{@name}' || fail "format in session name not expanded"
must_equal "$($TMUX display-message -p '#{session_name}')" 'format#ok'
$TMUX rename-window '#{@name}' || fail "format in window name not expanded"
must_equal "$($TMUX display-message -p '#{window_name}')" 'format#ok'
must_fail $TMUX rename-session '#{session_name}:bad'
must_fail $TMUX rename-window '#{window_name}.bad'
pid=$($TMUX display-message -p '#{pid}')
created=$($TMUX new-session -dP -F '#{session_id}:#{window_id}' \
-s 'new-session#ok' -n 'new-window#ok') || \
fail "new-session name with # rejected"
created_session=${created%:*}
created_window=${created#*:}
must_equal "$($TMUX display-message -pt "$created_session" '#{session_name}')" \
'new-session#ok'
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
'new-window#ok'
$TMUX kill-session -t "$created_session"
must_fail $TMUX new-session -d -s 'new-session:bad'
must_fail $TMUX new-session -d -s 'new-session.bad'
must_fail $TMUX new-session -d -n 'new-window:bad'
must_fail $TMUX new-session -d -n 'new-window.bad'
created_window=$($TMUX new-window -dP -F '#{window_id}' \
-n 'created-window#ok') || \
fail "new-window name with # rejected"
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
'created-window#ok'
must_fail $TMUX new-window -d -n 'created-window:bad'
must_fail $TMUX new-window -d -n 'created-window.bad'
created=$($TMUX new-session -dP -F '#{session_id}:#{window_id}' \
-s 'new-session-#{pid}' -n 'new-window-#{pid}') || \
fail "format in new-session name not expanded"
created_session=${created%:*}
created_window=${created#*:}
must_equal "$($TMUX display-message -pt "$created_session" '#{session_name}')" \
"new-session-$pid"
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
"new-window-$pid"
$TMUX kill-session -t "$created_session"
created_window=$($TMUX new-window -dP -F '#{window_id}' -n '#{@name}') || \
fail "format in new-window name not expanded"
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
'format#ok'
must_fail $TMUX new-session -d -s 'new-session-#{pid}:bad'
must_fail $TMUX new-session -d -n 'new-window-#{pid}.bad'
must_fail $TMUX new-window -d -n '#{window_name}:bad'
# Invalid UTF-8 is never allowed for command names.
invalid=$(printf '\302')
must_fail $TMUX rename-session "bad${invalid}name"
must_fail $TMUX rename-window "bad${invalid}name"
must_fail $TMUX new-session -d -s "bad${invalid}name"
must_fail $TMUX new-session -d -n "bad${invalid}name"
must_fail $TMUX new-window -d -n "bad${invalid}name"
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'
# 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.
$TMUX send-keys "printf '\\033kescape#:.ok\\033\\\\'" Enter || exit 1
sleep 1
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'
# Invalid UTF-8 from escape sequences is ignored.
$TMUX rename-window 'before-invalid' || exit 1
$TMUX send-keys "printf '\\033kbad\\302name\\033\\\\'" Enter || exit 1
sleep 1
must_equal "$($TMUX display-message -p '#{window_name}')" 'before-invalid'
$TMUX select-pane -T 'before-invalid-title' || exit 1
$TMUX send-keys "printf '\\033]2;bad\\302title\\007'" Enter || exit 1
sleep 1
must_equal "$($TMUX display-message -p '#{pane_title}')" 'before-invalid-title'
$TMUX kill-server 2>/dev/null
exit 0

View File

@@ -1,20 +0,0 @@
#!/bin/sh
# command-alias expansion
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
$TMUX new-session -d -sfoo || exit 1
$TMUX split-window -d -tfoo:0.0 || exit 1
$TMUX set -s command-alias[100] zoom='resize-pane -Z' || exit 1
$TMUX zoom -tfoo:0.0 || exit 1
[ "$($TMUX display-message -p -tfoo:0.0 '#{window_zoomed_flag}')" = 1 ] || exit 1
$TMUX kill-server 2>/dev/null
exit 0

View File

@@ -1,203 +0,0 @@
#!/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

View File

@@ -67,7 +67,6 @@ $TMUX set @true 1 || exit 1
$TMUX set @false 0 || exit 1 $TMUX set @false 0 || exit 1
$TMUX set @warm Summer || exit 1 $TMUX set @warm Summer || exit 1
$TMUX set @cold Winter || exit 1 $TMUX set @cold Winter || exit 1
$TMUX set @v 'foo:bar' || exit 1
# Plain string without substitutions et al # Plain string without substitutions et al
test_format "abc xyz" "abc xyz" test_format "abc xyz" "abc xyz"
@@ -75,8 +74,6 @@ test_format "abc xyz" "abc xyz"
# Test basic escapes for "#", "{", "#{" "}", "#}", "," # Test basic escapes for "#", "{", "#{" "}", "#}", ","
test_format "##" "#" test_format "##" "#"
test_format "#," "," test_format "#," ","
test_format "#{s/#:/_/:@v}" "foo_bar"
test_format "#{s/o/#:/:@v}" "f:::bar"
test_format "{" "{" test_format "{" "{"
test_format "##{" "#{" test_format "##{" "#{"
test_format "#}" "}" test_format "#}" "}"

View File

@@ -1,300 +0,0 @@
#!/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

View File

@@ -1,336 +0,0 @@
#!/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,79 +0,0 @@
#!/bin/sh
# Exercise screen-redraw.c bidirectional-isolate handling. When the client is
# UTF-8 and its terminal has the Bidi capability, each drawn span is wrapped in
# directional isolate characters (U+2066 .. U+2069) so a bidi terminal does not
# reorder pane contents across borders (the REDRAW_ISOLATES path).
#
# The Bidi capability is added with terminal-overrides before the client
# attaches, so the attached client picks it up. The isolate characters appear in
# the captured output around each span.
#
# Run with GENERATE=1 to (re)create the golden files.
PATH=/bin:/usr/bin
TERM=screen
LC_ALL=C.UTF-8
export TERM LC_ALL
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
RESULTS=screen-redraw-results
TMP=$(mktemp)
trap "rm -f $TMP; $TMUX kill-server 2>/dev/null; $TMUX2 kill-server 2>/dev/null" \
0 1 15
fail() {
echo "$*" >&2
exit 1
}
compare() {
sleep 1
$TMUX capturep -p >$TMP || exit 1
if [ -n "$GENERATE" ]; then
cp $TMP "$RESULTS/$1.result" || exit 1
echo "generated $1"
else
cmp -s $TMP "$RESULTS/$1.result" || \
fail "scene $1 differs from $RESULTS/$1.result"
fi
}
new_scene() {
$TMUX2 neww -d "sh -c 'printf \"BIDI-LEFT 12345\nBIDI-RIGHT 67890\"; exec sleep 100'" || exit 1
$TMUX2 selectw -t:\$ || exit 1
$TMUX2 resizew -x40 -y12 || exit 1
}
C="sh -c 'printf \"BIDI-PANE 12345\nBIDI-PANE 67890\"; exec sleep 100'"
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
$TMUX2 new -d -x40 -y12 "sh -c 'printf \"BIDI-BASE 12345\nBIDI-BASE 67890\"; exec sleep 100'" || exit 1
$TMUX2 set -g status off || exit 1
$TMUX2 set -g window-size manual || exit 1
# Add the Bidi capability before the client attaches.
$TMUX2 set -ag terminal-overrides ",*:Bidi=\\E[8h" || exit 1
$TMUX new -d -x40 -y12 || exit 1
$TMUX set -g status off || exit 1
$TMUX set -g window-size manual || exit 1
$TMUX set -g default-terminal "tmux-256color" || exit 1
$TMUX send -l "$TMUX2 attach" || exit 1
$TMUX send Enter || exit 1
sleep 1
# Single pane: content is wrapped in isolates.
new_scene
compare bidi-single
# Two panes: borders and both panes are isolated.
new_scene
$TMUX2 splitw -h "$C" || exit 1
compare bidi-split
exit 0

View File

@@ -1,162 +0,0 @@
#!/bin/sh
# Exercise the scene caching in screen-redraw.c. A scene is built once and reused
# until it is invalidated; redraw_get_scene rebuilds it when the window changes,
# the generation number is bumped (panes moved/resized/swapped), or the offset
# changes. These tests make such a change in place and capture afterwards: if the
# matching invalidation did not fire, the stale cached scene would be drawn and
# the capture would not match the golden.
#
# Each scene is rendered in an inner tmux attached inside an outer tmux pane. The
# outer pane is captured and compared with a golden in screen-redraw-results/.
#
# Run with GENERATE=1 to (re)create the golden files.
PATH=/bin:/usr/bin
TERM=screen
LC_ALL=C.UTF-8
export TERM LC_ALL
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
RESULTS=screen-redraw-results
TMP=$(mktemp)
trap "rm -f $TMP; $TMUX kill-server 2>/dev/null; $TMUX2 kill-server 2>/dev/null" \
0 1 15
fail() {
echo "$*" >&2
exit 1
}
compare() {
sleep 1
$TMUX capturep -p >$TMP || exit 1
if [ -n "$GENERATE" ]; then
cp $TMP "$RESULTS/$1.result" || exit 1
echo "generated $1"
else
cmp -s $TMP "$RESULTS/$1.result" || \
fail "scene $1 differs from $RESULTS/$1.result"
fi
}
new_scene() {
$TMUX2 neww -d "sh -c 'i=0; while [ \$i -lt 13 ]; do printf \"BASE%02d abcdefghij\n\" \$i; i=\$((i + 1)); done; exec sleep 100'" || exit 1
$TMUX2 selectw -t:\$ || exit 1
$TMUX2 resizew -x40 -y14 || exit 1
}
C="sh -c 'i=0; while [ \$i -lt 5 ]; do printf \"PANE%02d abcdefghij\n\" \$i; i=\$((i + 1)); done; exec sleep 100'"
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
$TMUX2 new -d -x40 -y14 "sh -c 'exec sleep 100'" || exit 1
$TMUX2 set -g status off || exit 1
$TMUX2 set -g window-size manual || exit 1
$TMUX new -d -x40 -y14 || exit 1
$TMUX set -g status off || exit 1
$TMUX set -g window-size manual || exit 1
$TMUX set -g default-terminal "tmux-256color" || exit 1
$TMUX send -l "$TMUX2 attach" || exit 1
$TMUX send Enter || exit 1
sleep 1
# --- Generation change: a pane is resized in place. ---
# Two columns; first capture pins the initial divider position. The scene is
# built here and cached.
new_scene
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
compare cache-resize-before
# Move the divider left. resize-pane bumps the generation, so the cached scene
# must be rebuilt and the divider must appear in its new position.
$TMUX2 resize-pane -t0 -L 6 || exit 1
compare cache-resize-after
# --- Generation change: panes are swapped/rotated in place. ---
# Three columns, each pane titled so the order is visible. The pane status line
# makes a swap show up in the captured scene.
new_scene
$TMUX2 setw pane-border-format " #{pane_title} " || exit 1
$TMUX2 setw pane-border-status top || exit 1
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 selectp -t:.0 -T AAA || exit 1
$TMUX2 selectp -t:.1 -T BBB || exit 1
$TMUX2 selectp -t:.2 -T CCC || exit 1
$TMUX2 selectp -t:.0 || exit 1
compare cache-rotate-before
# Rotate the panes; the titles must move with them in the rebuilt scene.
# rotate-window keeps the cell geometry, so only its scene invalidation makes
# this differ from cache-rotate-before.
$TMUX2 rotate-window || exit 1
$TMUX2 selectp -t:.0 || exit 1
compare cache-rotate-after
# --- Generation change: two panes swapped in place. ---
# swap-pane likewise moves panes between cells without changing geometry; the
# swapped titles must appear in the rebuilt scene.
new_scene
$TMUX2 setw pane-border-format " #{pane_title} " || exit 1
$TMUX2 setw pane-border-status top || exit 1
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 selectp -t:.0 -T LEFT || exit 1
$TMUX2 selectp -t:.1 -T RIGHT || exit 1
$TMUX2 selectp -t:.0 || exit 1
compare cache-swap-before
$TMUX2 swap-pane -d -s:.0 -t:.1 || exit 1
$TMUX2 selectp -t:.0 || exit 1
compare cache-swap-after
# --- Window change: the client switches between two differently laid out
# windows and back. The scene is keyed on the window, so each must show its own
# layout and switching back must not show the other window's cached scene. ---
# Window A: a single pane (no internal border).
$TMUX2 neww -d "sh -c 'i=0; while [ \$i -lt 13 ]; do printf \"WINA%02d abcdefghij\n\" \$i; i=\$((i + 1)); done; exec sleep 100'" || exit 1
$TMUX2 selectw -t:\$ || exit 1
$TMUX2 resizew -x40 -y14 || exit 1
A=$($TMUX2 display -p '#{window_id}') || exit 1
# Window B: a top/bottom split (a horizontal border).
$TMUX2 neww -d "sh -c 'i=0; while [ \$i -lt 13 ]; do printf \"WINB%02d abcdefghij\n\" \$i; i=\$((i + 1)); done; exec sleep 100'" || exit 1
$TMUX2 selectw -t:\$ || exit 1
$TMUX2 resizew -x40 -y14 || exit 1
$TMUX2 splitw -v "$C" || exit 1
B=$($TMUX2 display -p '#{window_id}') || exit 1
$TMUX2 selectw -t$A || exit 1
compare cache-window-a
$TMUX2 selectw -t$B || exit 1
compare cache-window-b
$TMUX2 selectw -t$A || exit 1
compare cache-window-a-again
# --- Size change: the client (and so the scene) is resized. ---
# With window-size latest the inner window tracks the client size, so resizing
# the outer window resizes the inner client and the scene. The cached scene must
# be rebuilt at the new size (the scene->sx/sy mismatch path in
# redraw_get_scene), not drawn at the old size.
$TMUX2 set -g window-size latest || exit 1
$TMUX2 neww -d "sh -c 'i=0; while [ \$i -lt 13 ]; do printf \"SIZE%02d abcdefghij\n\" \$i; i=\$((i + 1)); done; exec sleep 100'" || exit 1
$TMUX2 selectw -t:\$ || exit 1
$TMUX2 splitw -v "$C" || exit 1
compare cache-resizeclient-before
$TMUX resizew -x30 -y10 || exit 1
compare cache-resizeclient-after
exit 0

View File

@@ -1,221 +0,0 @@
#!/bin/sh
# Exercise screen-redraw.c drawing of floating panes: their borders and titles,
# clipping at the window edge, and the interaction with the area outside the
# window (when the window is smaller than the attached client).
#
# Each scene is rendered in an inner tmux attached inside an outer tmux pane.
# The outer pane is captured (the full client scene drawn by screen-redraw.c)
# and compared with a golden file in screen_redraw_results/.
#
# Run with GENERATE=1 to (re)create the golden files.
PATH=/bin:/usr/bin
TERM=screen
LC_ALL=C.UTF-8
export TERM LC_ALL
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
RESULTS=screen-redraw-results
TMP=$(mktemp)
trap "rm -f $TMP; $TMUX kill-server 2>/dev/null; $TMUX2 kill-server 2>/dev/null" \
0 1 15
fail() {
echo "$*" >&2
exit 1
}
compare() {
sleep 1
$TMUX capturep -p $2 >$TMP || exit 1
if [ -n "$GENERATE" ]; then
cp $TMP "$RESULTS/$1.result" || exit 1
echo "generated $1"
else
cmp -s $TMP "$RESULTS/$1.result" || \
fail "scene $1 differs from $RESULTS/$1.result"
fi
}
# new_scene <width> <height>: fresh inner window of the given window size.
new_scene() {
$TMUX2 neww -d "sh -c 'printf base; exec sleep 100'" || exit 1
$TMUX2 selectw -t:\$ || exit 1
$TMUX2 resizew -x$1 -y$2 || exit 1
}
# tile_2x2: split the current window into a 2x2 grid of tiled panes.
tile_2x2() {
C="sh -c 'exec sleep 100'"
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 splitw -v "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
$TMUX2 splitw -v "$C" || exit 1
$TMUX2 select-layout tiled || exit 1
}
# fill_base_pattern: make clipped floating panes visibly cover every column.
fill_base_pattern() {
$TMUX2 respawnp -k "sh -c 'i=0; while [ \$i -lt 12 ]; do printf \"%02d:abcdefghijklmnopqrstuvwxyz0123456789\n\" \$i; i=\$((i + 1)); done; exec sleep 100'" || exit 1
}
# fill_all_panes: make each pane visibly nonblank.
fill_all_panes() {
for pane in $($TMUX2 list-panes -F '#{pane_id}'); do
$TMUX2 respawnp -k -t "$pane" "sh -c 'i=0; while [ \$i -lt 5 ]; do printf \"TILE%02d abcdefghij\n\" \$i; i=\$((i + 1)); done; exec sleep 100'" || exit 1
done
}
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
$TMUX2 new -d -x40 -y12 "sh -c 'printf base; exec sleep 100'" || exit 1
$TMUX2 set -g status off || exit 1
$TMUX2 set -g window-size manual || exit 1
$TMUX2 set -g pane-border-format " #{pane_title} " || exit 1
$TMUX new -d -x40 -y12 || exit 1
$TMUX set -g status off || exit 1
$TMUX set -g window-size manual || exit 1
$TMUX set -g default-terminal "tmux-256color" || exit 1
$TMUX send -l "$TMUX2 attach" || exit 1
$TMUX send Enter || exit 1
sleep 1
# Basic floating pane, well inside the window.
new_scene 40 12
$TMUX2 new-pane -x20 -y6 -X8 -Y3 "sh -c 'printf FLOAT; exec sleep 100'" || exit 1
compare floating-basic
# Floating pane with a title on its border. pane-border-status also draws the
# base pane's title, so set it explicitly (the default is the hostname, which is
# not stable).
new_scene 40 12
$TMUX2 setw pane-border-status top || exit 1
$TMUX2 selectp -t:.0 -T base || exit 1
$TMUX2 new-pane -x20 -y6 -X8 -Y2 -T title \
"sh -c 'printf FLOAT; exec sleep 100'" || exit 1
compare floating-title
# Larger floating pane with double border lines.
new_scene 40 12
$TMUX2 new-pane -x28 -y8 -X4 -Y1 -B double \
"sh -c 'printf FLOAT; exec sleep 100'" || exit 1
compare floating-border-double
# Floating pane with no border lines: redraw_mark_pane_borders returns early so
# the float has no border at all, only its (clipped) content over the base pane.
new_scene 40 12
$TMUX2 new-pane -x20 -y6 -X10 -Y3 -B none \
"sh -c 'printf NOBORDER; exec sleep 100'" || exit 1
compare floating-noborder
# Floating pane positioned past the right and bottom edges: must clip.
new_scene 40 12
$TMUX2 new-pane -x20 -y6 -X30 -Y8 "sh -c 'printf CLIP; exec sleep 100'" || exit 1
compare floating-clip-edge
# Window smaller than the client (outside area filled), with a floating pane
# that overlaps the boundary into the outside region.
new_scene 28 8
$TMUX2 new-pane -x16 -y5 -X18 -Y4 "sh -c 'printf OUT; exec sleep 100'" || exit 1
compare floating-outside
# Floating pane clipped at the top-left corner (negative offsets).
new_scene 40 12
$TMUX2 new-pane -x18 -y6 -X-4 -Y-2 "sh -c 'printf TL; exec sleep 100'" || exit 1
compare floating-clip-topleft
# Floating panes clipped at each edge. The patterned base pane makes sure the
# clipped edge still obscures both content and borders underneath.
new_scene 40 12
fill_base_pattern
$TMUX2 new-pane -x20 -y6 -X-10 -Y3 "sh -c 'printf LEFT; exec sleep 100'" || exit 1
compare floating-clip-left
new_scene 40 12
fill_base_pattern
$TMUX2 new-pane -x20 -y6 -X30 -Y3 "sh -c 'printf RIGHT; exec sleep 100'" || exit 1
compare floating-clip-right
new_scene 40 12
fill_base_pattern
$TMUX2 new-pane -x20 -y6 -X10 -Y-3 "sh -c 'printf TOP; exec sleep 100'" || exit 1
compare floating-clip-top
new_scene 40 12
fill_base_pattern
$TMUX2 new-pane -x20 -y6 -X10 -Y9 "sh -c 'printf BOTTOM; exec sleep 100'" || exit 1
compare floating-clip-bottom
# Floating pane over a tiled 2x2 grid: the float draws a complete box and must
# NOT merge its borders with the tiled pane borders underneath.
new_scene 40 12
tile_2x2
fill_all_panes
$TMUX2 new-pane -x16 -y6 -X11 -Y3 "sh -c 'printf FLT; exec sleep 100'" || exit 1
compare floating-over-tiled
# Same, but the float uses double border lines while the tiled panes use single:
# the two border styles must coexist without merging.
new_scene 40 12
tile_2x2
fill_all_panes
$TMUX2 new-pane -x16 -y6 -X11 -Y3 -B double \
"sh -c 'printf FLT; exec sleep 100'" || exit 1
compare floating-over-tiled-double
# Two overlapping floating panes: the later (top) float draws over the earlier.
new_scene 40 12
$TMUX2 new-pane -x16 -y6 -X4 -Y2 "sh -c 'printf AAA; exec sleep 100'" || exit 1
$TMUX2 new-pane -x16 -y6 -X14 -Y6 "sh -c 'printf BBB; exec sleep 100'" || exit 1
compare floating-overlap
# Two floating panes with different per-pane configuration: one has its status on
# top with single borders, the other has its status on the bottom with heavy
# borders (pane-border-status and pane-border-lines are per-pane options).
new_scene 40 12
$TMUX2 setw pane-border-format " #{pane_title} " || exit 1
$TMUX2 new-pane -x16 -y4 -X3 -Y1 -T one \
"sh -c 'printf ONE; exec sleep 100'" || exit 1
$TMUX2 set -p pane-border-status top || exit 1
$TMUX2 set -p pane-border-lines single || exit 1
$TMUX2 new-pane -x16 -y4 -X18 -Y6 -T two \
"sh -c 'printf TWO; exec sleep 100'" || exit 1
$TMUX2 set -p pane-border-status bottom || exit 1
$TMUX2 set -p pane-border-lines heavy || exit 1
compare floating-mixed-config
# Floating pane over a pane that has a scrollbar: the float must draw over the
# scrollbar. Captured with -e since the scrollbar is drawn with styles.
new_scene 40 12
$TMUX2 setw pane-scrollbars on || exit 1
$TMUX2 setw pane-scrollbars-style "bg=black,fg=white,width=1,pad=0" || exit 1
$TMUX2 new-pane -x16 -y6 -X22 -Y3 "sh -c 'printf OVERSB; exec sleep 100'" || exit 1
compare floating-over-scrollbar -e
# Floating pane overlapping the client status line: the status line is not part
# of the window scene, so the float is clipped at the window's bottom edge.
new_scene 40 11
$TMUX2 set status on || exit 1
$TMUX2 set status-position bottom || exit 1
$TMUX2 set status-format[0] "" || exit 1
$TMUX2 new-pane -x20 -y6 -X8 -Y7 "sh -c 'printf OVERST; exec sleep 100'" || exit 1
compare floating-over-status
# A window left with only a floating pane: killing the single tiled pane removes
# it from the layout, so the area it occupied is no longer owned by any pane and
# is drawn as EMPTY cells (middle dots) around the float. This is the only way to
# produce a REDRAW_SPAN_EMPTY span.
new_scene 40 12
$TMUX2 new-pane -x20 -y6 -X8 -Y3 "sh -c 'printf FLOAT; exec sleep 100'" || exit 1
tiled=$($TMUX2 list-panes -F '#{pane_floating_flag} #{pane_id}' | \
awk '$1==0{print $2; exit}') || exit 1
$TMUX2 kill-pane -t "$tiled" || exit 1
compare floating-empty
exit 0

View File

@@ -1,212 +0,0 @@
#!/bin/sh
# Exercise screen-redraw.c pane-border-indicators: the arrow indicators that
# point at the active pane, and the two-pane border colour split.
#
# Arrows are drawn as glyphs (captured plain). The two-pane colour split is
# drawn with styles, so that scene is captured with escapes (-e) and uses
# distinct active/inactive border styles so the split is visible.
#
# Each scene is rendered in an inner tmux attached inside an outer tmux pane.
# The outer pane is captured and compared with a golden in screen-redraw-results/.
#
# Run with GENERATE=1 to (re)create the golden files.
PATH=/bin:/usr/bin
TERM=screen
LC_ALL=C.UTF-8
export TERM LC_ALL
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
RESULTS=screen-redraw-results
TMP=$(mktemp)
trap "rm -f $TMP; $TMUX kill-server 2>/dev/null; $TMUX2 kill-server 2>/dev/null" \
0 1 15
fail() {
echo "$*" >&2
exit 1
}
# compare <name> [-e]: capture the outer pane and compare (or generate).
compare() {
sleep 1
$TMUX capturep -p $2 >$TMP || exit 1
if [ -n "$GENERATE" ]; then
cp $TMP "$RESULTS/$1.result" || exit 1
echo "generated $1"
else
cmp -s $TMP "$RESULTS/$1.result" || \
fail "scene $1 differs from $RESULTS/$1.result"
fi
}
new_scene() {
$TMUX2 neww -d "sh -c 'exec sleep 100'" || exit 1
$TMUX2 selectw -t:\$ || exit 1
$TMUX2 resizew -x40 -y12 || exit 1
}
C="sh -c 'exec sleep 100'"
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
$TMUX2 new -d -x40 -y12 "sh -c 'exec sleep 100'" || exit 1
$TMUX2 set -g status off || exit 1
$TMUX2 set -g window-size manual || exit 1
$TMUX new -d -x40 -y12 || exit 1
$TMUX set -g status off || exit 1
$TMUX set -g window-size manual || exit 1
$TMUX set -g default-terminal "tmux-256color" || exit 1
$TMUX send -l "$TMUX2 attach" || exit 1
$TMUX send Enter || exit 1
sleep 1
# --- Arrows: must appear for whichever pane is active (GitHub #4780). ---
$TMUX2 set -g pane-border-indicators arrows || exit 1
# Two panes, left active: arrow points left.
new_scene
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
compare arrows-2pane-left
# Two panes, right active: arrow points right (the case that regressed).
new_scene
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 selectp -t1 || exit 1
compare arrows-2pane-right
# Three columns, middle active.
new_scene
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 selectp -t1 || exit 1
compare arrows-3pane
# Four panes (2x2), one active.
new_scene
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 splitw -v "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
$TMUX2 splitw -v "$C" || exit 1
$TMUX2 select-layout tiled || exit 1
$TMUX2 selectp -t0 || exit 1
compare arrows-4pane
# --- Two-pane border colour split. ---
# Distinct active/inactive styles so the coloured halves are visible; captured
# with -e to record the SGR.
$TMUX2 set -g pane-border-indicators colour || exit 1
# Left/right split: the border is vertical and is split into a top half (active
# pane colour) and a bottom half (the LAYOUT_LEFTRIGHT case).
new_scene
$TMUX2 setw pane-active-border-style fg=red || exit 1
$TMUX2 setw pane-border-style fg=green || exit 1
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
compare two-pane-colour-vertical -e
# Top/bottom split: the border is horizontal and is split into a left half and a
# right half instead (the LAYOUT_TOPBOTTOM case).
new_scene
$TMUX2 setw pane-active-border-style fg=red || exit 1
$TMUX2 setw pane-border-style fg=green || exit 1
$TMUX2 splitw -v "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
compare two-pane-colour-horizontal -e
# The colour split only applies with exactly two tiled panes. With three panes
# the split is suppressed: the whole border uses the active pane's colour and
# there is no coloured half (redraw_check_two_pane_colours returns 0).
new_scene
$TMUX2 setw pane-active-border-style fg=red || exit 1
$TMUX2 setw pane-border-style fg=green || exit 1
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
compare colour-three-suppressed -e
# A floating pane is ignored by the two-pane count, so two tiled panes plus a
# float still split. The float itself is never coloured by the indicator.
new_scene
$TMUX2 setw pane-active-border-style fg=red || exit 1
$TMUX2 setw pane-border-style fg=green || exit 1
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
$TMUX2 new-pane -x16 -y6 -X10 -Y2 "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
compare colour-two-plus-float -e
# --- Both indicators together. ---
# pane-border-indicators both enables the arrow and the colour split at once
# (it satisfies both the arrow and the colour branches). Two panes so the colour
# split also applies.
$TMUX2 set -g pane-border-indicators both || exit 1
new_scene
$TMUX2 setw pane-active-border-style fg=red || exit 1
$TMUX2 setw pane-border-style fg=green || exit 1
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
compare indicators-both -e
# --- Marked pane. ---
# A marked pane (select-pane -m) has its border drawn reversed. Captured with -e
# to record the reverse attribute. The marked pane is made non-active so its
# reversed border is distinct from the active pane.
$TMUX2 set -g pane-border-indicators off || exit 1
# Left/right split, right pane marked.
new_scene
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
$TMUX2 selectp -t1 -m || exit 1
compare marked-pane-lr -e
# Top/bottom split, bottom pane marked.
new_scene
$TMUX2 splitw -v "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
$TMUX2 selectp -t1 -m || exit 1
compare marked-pane-tb -e
# Three columns, middle pane marked (reversed border on both sides).
new_scene
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
$TMUX2 selectp -t1 -m || exit 1
compare marked-pane-three -e
# Floating pane marked: the whole floating box border is reversed.
new_scene
$TMUX2 new-pane -x20 -y6 -X8 -Y3 "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
$TMUX2 selectp -t1 -m || exit 1
compare marked-pane-float -e
# Marked pane together with a pane status line: the title border is reversed too.
new_scene
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 setw pane-border-format " #{pane_index}:#{pane_title} " || exit 1
$TMUX2 setw pane-border-status top || exit 1
$TMUX2 selectp -t:.0 -T left || exit 1
$TMUX2 selectp -t:.1 -T right || exit 1
$TMUX2 selectp -t0 || exit 1
$TMUX2 selectp -t1 -m || exit 1
compare marked-pane-status -e
exit 0

View File

@@ -1,116 +0,0 @@
#!/bin/sh
# Exercise screen-redraw.c when the window is smaller than the attached client,
# so part of the client is outside the window. screen-redraw.c fills the outside
# area and draws a real border along the window's right and/or bottom edge (not
# just where panes meet). This checks redraw_get_window_offset and the OUTSIDE
# span handling, including how the window-edge border joins the pane borders.
#
# Each scene is rendered in an inner tmux attached inside an outer tmux pane.
# The outer client is 40x14; the inner window is made smaller with resizew. The
# outer pane is captured and compared with a golden in screen-redraw-results/.
#
# Run with GENERATE=1 to (re)create the golden files.
PATH=/bin:/usr/bin
TERM=screen
LC_ALL=C.UTF-8
export TERM LC_ALL
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
RESULTS=screen-redraw-results
TMP=$(mktemp)
trap "rm -f $TMP; $TMUX kill-server 2>/dev/null; $TMUX2 kill-server 2>/dev/null" \
0 1 15
fail() {
echo "$*" >&2
exit 1
}
compare() {
sleep 1
$TMUX capturep -p >$TMP || exit 1
if [ -n "$GENERATE" ]; then
cp $TMP "$RESULTS/$1.result" || exit 1
echo "generated $1"
else
cmp -s $TMP "$RESULTS/$1.result" || \
fail "scene $1 differs from $RESULTS/$1.result"
fi
}
# new_scene <width> <height>: fresh inner window smaller than the 40x14 client.
new_scene() {
$TMUX2 neww -d "sh -c 'i=0; while [ \$i -lt $(($2 - 1)) ]; do printf \"OUT%02d abcdefghij\n\" \$i; i=\$((i + 1)); done; exec sleep 100'" || exit 1
$TMUX2 selectw -t:\$ || exit 1
$TMUX2 resizew -x$1 -y$2 || exit 1
}
C="sh -c 'i=0; while [ \$i -lt 8 ]; do printf \"PAN%02d abcdefghij\n\" \$i; i=\$((i + 1)); done; exec sleep 100'"
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
$TMUX2 new -d -x40 -y14 "sh -c 'i=0; while [ \$i -lt 13 ]; do printf \"OUT%02d abcdefghij\n\" \$i; i=\$((i + 1)); done; exec sleep 100'" || exit 1
$TMUX2 set -g status off || exit 1
$TMUX2 set -g window-size manual || exit 1
$TMUX new -d -x40 -y14 || exit 1
$TMUX set -g status off || exit 1
$TMUX set -g window-size manual || exit 1
$TMUX set -g default-terminal "tmux-256color" || exit 1
$TMUX send -l "$TMUX2 attach" || exit 1
$TMUX send Enter || exit 1
sleep 1
# Single pane, narrower than the client: a border along the right edge.
new_scene 28 14
compare outside-right-single
# Narrower than the client with a left/right split: the inter-pane border plus a
# real border on the window's right edge, then the outside area.
new_scene 28 14
$TMUX2 splitw -h "$C" || exit 1
compare outside-right-split
# Shorter than the client with a top/bottom split: a real border on the window's
# bottom edge.
new_scene 40 9
$TMUX2 splitw -v "$C" || exit 1
compare outside-bottom-split
# Smaller in both dimensions with a 2x2 grid: borders on the right and bottom
# edges meeting the internal pane borders at the corner.
new_scene 28 9
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 splitw -v "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
$TMUX2 splitw -v "$C" || exit 1
$TMUX2 select-layout tiled || exit 1
compare outside-both-2x2
# Window BIGGER than the client: only part of the window is viewed and the view
# can be panned (refresh-client). This exercises a non-zero scene offset.
# A 2x2 grid in a 60x20 window viewed through the 40x14 client.
new_scene 60 20
$TMUX2 splitw -h "$C" || exit 1
$TMUX2 splitw -v "$C" || exit 1
$TMUX2 selectp -t0 || exit 1
$TMUX2 splitw -v "$C" || exit 1
$TMUX2 select-layout tiled || exit 1
# Default view: the top-left of the window.
$TMUX2 refresh-client -U 100 || exit 1
$TMUX2 refresh-client -L 100 || exit 1
compare bigger-topleft
# Panned to the bottom-right of the window (down and right to the limit).
$TMUX2 refresh-client -D 100 || exit 1
$TMUX2 refresh-client -R 100 || exit 1
compare bigger-bottomright
exit 0

View File

@@ -1,92 +0,0 @@
#!/bin/sh
# Exercise drawing of popups (display-popup) over the window scene. A popup is an
# overlay drawn on top of the redraw scene (the overlay_draw path in
# screen-redraw.c), so this guards against regressions in how popups appear.
#
# A popup is modal and stays open until its command exits, so each scene fully
# re-creates the servers and re-attaches; the popup is opened in the background
# (display-popup blocks the client that runs it) and the outer pane is captured
# while it is open.
#
# Run with GENERATE=1 to (re)create the golden files.
PATH=/bin:/usr/bin
TERM=screen
LC_ALL=C.UTF-8
export TERM LC_ALL
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
RESULTS=screen-redraw-results
TMP=$(mktemp)
trap "rm -f $TMP; $TMUX kill-server 2>/dev/null; $TMUX2 kill-server 2>/dev/null" \
0 1 15
fail() {
echo "$*" >&2
exit 1
}
compare() {
sleep 1
$TMUX capturep -p >$TMP || exit 1
if [ -n "$GENERATE" ]; then
cp $TMP "$RESULTS/$1.result" || exit 1
echo "generated $1"
else
cmp -s $TMP "$RESULTS/$1.result" || \
fail "scene $1 differs from $RESULTS/$1.result"
fi
}
C="sh -c 'i=0; while [ \$i -lt 13 ]; do printf \"POP%02d abcdefghij\n\" \$i; i=\$((i + 1)); done; exec sleep 100'"
# setup: fresh inner window attached inside a fresh outer pane, 40x14.
setup() {
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
$TMUX2 new -d -x40 -y14 "$C" || exit 1
$TMUX2 set -g status off || exit 1
$TMUX2 set -g window-size manual || exit 1
$TMUX2 resizew -x40 -y14 || exit 1
$TMUX new -d -x40 -y14 || exit 1
$TMUX set -g status off || exit 1
$TMUX set -g window-size manual || exit 1
$TMUX set -g default-terminal "tmux-256color" || exit 1
$TMUX send -l "$TMUX2 attach" || exit 1
$TMUX send Enter || exit 1
sleep 1
}
# popup <args>: open a popup running a fixed command, in the background (it stays
# open because the command sleeps; the servers are killed at the next setup).
popup() {
$TMUX2 display-popup "$@" -E "sh -c 'printf POPUP; exec sleep 100'" &
sleep 1
}
# Basic popup over a single pane.
setup
popup -w20 -h6 -x6 -y3
compare popup-basic
# Popup over a split: drawn on top of the pane border.
setup
$TMUX2 splitw -h "$C" || exit 1
popup -w24 -h8 -x8 -y3
compare popup-over-split
# Popup with no border lines (-B).
setup
popup -B -w20 -h6 -x6 -y3
compare popup-noborder
# Popup with double border lines.
setup
popup -b double -w20 -h6 -x6 -y3
compare popup-double
exit 0

View File

@@ -1,12 +0,0 @@

View File

@@ -1,12 +0,0 @@

View File

@@ -1,12 +0,0 @@
│ │
→ ←
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │

View File

@@ -1,12 +0,0 @@
─↑─────────────────┼────────────────────

View File

@@ -1,12 +0,0 @@
BIDI-LEFT 12345
BIDI-RIGHT 67890

View File

@@ -1,12 +0,0 @@
BIDI-LEFT 12345 BIDI-PANE 12345
BIDI-RIGHT 67890 BIDI-PANE 67890
⁩│⁦
⁩│⁦
⁩│⁦
⁩│⁦
⁩│⁦
⁩│⁦
⁩│⁦
⁩│⁦
⁩│⁦
⁩│⁦

View File

@@ -1,14 +0,0 @@
│PAN06 abcdefghij
│PAN07 abcdefghij
─────────┼──────────────────────────────
│PAN00 abcdefghij
│PAN01 abcdefghij
│PAN02 abcdefghij
│PAN03 abcdefghij
│PAN04 abcdefghij
│PAN05 abcdefghij
│PAN06 abcdefghij
│PAN07 abcdefghij

View File

@@ -1,14 +0,0 @@
OUT11 abcdefghij │PAN00 abcd
OUT12 abcdefghij │PAN01 abcd
OUT13 abcdefghij │PAN02 abcd
OUT14 abcdefghij │PAN03 abcd
OUT15 abcdefghij │PAN04 abcd
OUT16 abcdefghij │PAN05 abcd
OUT17 abcdefghij │PAN06 abcd
OUT18 abcdefghij │PAN07 abcd
─────────────────────────────┼──────────
PAN00 abcdefghij │PAN00 abcd
PAN01 abcdefghij │PAN01 abcd
PAN02 abcdefghij │PAN02 abcd
PAN03 abcdefghij │PAN03 abcd

View File

@@ -1,14 +0,0 @@
hij │PANE00 abcdefghij
BASE07 abcdefg│PANE01 abcdefghij
hij │PANE02 abcdefghij
BASE08 abcdefg│PANE03 abcdefghij
hij │PANE04 abcdefghij
BASE09 abcdefg│
hij │
BASE10 abcdefg│
hij │
BASE11 abcdefg│
hij │
BASE12 abcdefg│
hij │

View File

@@ -1,14 +0,0 @@
BASE00 abcdefghij │PANE00 abcdefghij
BASE01 abcdefghij │PANE01 abcdefghij
BASE02 abcdefghij │PANE02 abcdefghij
BASE03 abcdefghij │PANE03 abcdefghij
BASE04 abcdefghij │PANE04 abcdefghij
BASE05 abcdefghij │
BASE06 abcdefghij │
BASE07 abcdefghij │
BASE08 abcdefghij │
BASE09 abcdefghij │
BASE10 abcdefghij │
BASE11 abcdefghij │
BASE12 abcdefghij │

View File

@@ -1,10 +0,0 @@
SIZE09 abcdefghij
SIZE10 abcdefghij
SIZE11 abcdefghij
SIZE12 abcdefghij
──────────────────────────────
PANE02 abcdefghij
PANE03 abcdefghij
PANE04 abcdefghij

View File

@@ -1,14 +0,0 @@
SIZE07 abcdefghij
SIZE08 abcdefghij
SIZE09 abcdefghij
SIZE10 abcdefghij
SIZE11 abcdefghij
SIZE12 abcdefghij
────────────────────────────────────────
PANE00 abcdefghij
PANE01 abcdefghij
PANE02 abcdefghij
PANE03 abcdefghij
PANE04 abcdefghij

View File

@@ -1,14 +0,0 @@
── BBB ───┬── CCC ──┬── AAA ────────────
PANE00 abc│cdefghij │BASE01 abcdefghij
defghij │PANE03 ab│BASE02 abcdefghij
PANE01 abc│cdefghij │BASE03 abcdefghij
defghij │PANE04 ab│BASE04 abcdefghij
PANE02 abc│cdefghij │BASE05 abcdefghij
defghij │ │BASE06 abcdefghij
PANE03 abc│ │BASE07 abcdefghij
defghij │ │BASE08 abcdefghij
PANE04 abc│ │BASE09 abcdefghij
defghij │ │BASE10 abcdefghij
│ │BASE11 abcdefghij
│ │BASE12 abcdefghij
│ │

View File

@@ -1,14 +0,0 @@
── AAA ───┬── BBB ──┬── CCC ────────────
BASE07 abc│PANE00 ab│PANE00 abcdefghij
defghij │cdefghij │PANE01 abcdefghij
BASE08 abc│PANE01 ab│PANE02 abcdefghij
defghij │cdefghij │PANE03 abcdefghij
BASE09 abc│PANE02 ab│PANE04 abcdefghij
defghij │cdefghij │
BASE10 abc│PANE03 ab│
defghij │cdefghij │
BASE11 abc│PANE04 ab│
defghij │cdefghij │
BASE12 abc│ │
defghij │ │
│ │

View File

@@ -1,14 +0,0 @@
── RIGHT ───────────┬── LEFT ───────────
PANE00 abcdefghij │BASE01 abcdefghij
PANE01 abcdefghij │BASE02 abcdefghij
PANE02 abcdefghij │BASE03 abcdefghij
PANE03 abcdefghij │BASE04 abcdefghij
PANE04 abcdefghij │BASE05 abcdefghij
│BASE06 abcdefghij
│BASE07 abcdefghij
│BASE08 abcdefghij
│BASE09 abcdefghij
│BASE10 abcdefghij
│BASE11 abcdefghij
│BASE12 abcdefghij

View File

@@ -1,14 +0,0 @@
── LEFT ────────────┬── RIGHT ──────────
BASE01 abcdefghij │PANE00 abcdefghij
BASE02 abcdefghij │PANE01 abcdefghij
BASE03 abcdefghij │PANE02 abcdefghij
BASE04 abcdefghij │PANE03 abcdefghij
BASE05 abcdefghij │PANE04 abcdefghij
BASE06 abcdefghij │
BASE07 abcdefghij │
BASE08 abcdefghij │
BASE09 abcdefghij │
BASE10 abcdefghij │
BASE11 abcdefghij │
BASE12 abcdefghij │

View File

@@ -1,14 +0,0 @@
WINA00 abcdefghij
WINA01 abcdefghij
WINA02 abcdefghij
WINA03 abcdefghij
WINA04 abcdefghij
WINA05 abcdefghij
WINA06 abcdefghij
WINA07 abcdefghij
WINA08 abcdefghij
WINA09 abcdefghij
WINA10 abcdefghij
WINA11 abcdefghij
WINA12 abcdefghij

View File

@@ -1,14 +0,0 @@
WINA00 abcdefghij
WINA01 abcdefghij
WINA02 abcdefghij
WINA03 abcdefghij
WINA04 abcdefghij
WINA05 abcdefghij
WINA06 abcdefghij
WINA07 abcdefghij
WINA08 abcdefghij
WINA09 abcdefghij
WINA10 abcdefghij
WINA11 abcdefghij
WINA12 abcdefghij

View File

@@ -1,14 +0,0 @@
WINB07 abcdefghij
WINB08 abcdefghij
WINB09 abcdefghij
WINB10 abcdefghij
WINB11 abcdefghij
WINB12 abcdefghij
────────────────────────────────────────
PANE00 abcdefghij
PANE01 abcdefghij
PANE02 abcdefghij
PANE03 abcdefghij
PANE04 abcdefghij

View File

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

View File

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

View File

@@ -1,14 +0,0 @@
═══════════════════╬════════════════════

View File

@@ -1,14 +0,0 @@
━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━

View File

@@ -1,14 +0,0 @@

View File

@@ -1,14 +0,0 @@
1
1
1
1
1
1
0000000000000000000011111111111111111111
2
2
2
2
2
2
2

View File

@@ -1,14 +0,0 @@
|
|
|
|
|
|
-------------------+--------------------
|
|
|
|
|
|
|

View File

@@ -1,14 +0,0 @@
───────────────────┼────────────────────

View File

@@ -1,14 +0,0 @@

View File

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

View File

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

View File

@@ -1,12 +0,0 @@
01:abcdefghijklmnopqrstuvwxyz0123456789
02:abcdefghijklmnopqrstuvwxyz0123456789
03:abcdefghijklmnopqrstuvwxyz0123456789
04:abcdefghijklmnopqrstuvwxyz0123456789
05:abcdefghijklmnopqrstuvwxyz0123456789
06:abcdefghijklmnopqrstuvwxyz0123456789
07:abcdefghijklmnopqrstuvwxyz0123456789
08:abcdefghijklmnopqrstuvwxyz0123456789
09:abcdef┌────────────────────┐23456789
10:abcdef│BOTTOM │23456789
11:abcdef│ │23456789
│ │

View File

@@ -1,12 +0,0 @@
base
┌──────────
│CLIP

View File

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

View File

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

View File

@@ -1,12 +0,0 @@
01:abcdef│ │23456789
02:abcdef│ │23456789
03:abcdef│ │23456789
04:abcdef└────────────────────┘23456789
05:abcdefghijklmnopqrstuvwxyz0123456789
06:abcdefghijklmnopqrstuvwxyz0123456789
07:abcdefghijklmnopqrstuvwxyz0123456789
08:abcdefghijklmnopqrstuvwxyz0123456789
09:abcdefghijklmnopqrstuvwxyz0123456789
10:abcdefghijklmnopqrstuvwxyz0123456789
11:abcdefghijklmnopqrstuvwxyz0123456789

View File

@@ -1,12 +0,0 @@
──────────────┘

View File

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

View File

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

View File

@@ -1,12 +0,0 @@
base
NOBORDER

View File

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

Some files were not shown because too many files have changed in this diff Show More