Resolve merge conflicts.

This commit is contained in:
Michael Grant
2026-07-08 08:24:43 +01:00
113 changed files with 2399 additions and 787 deletions

View File

@@ -77,3 +77,11 @@ jobs:
cd regress
export ASAN_OPTIONS="abort_on_error=1:detect_leaks=0"
${{ matrix.make }}
- name: logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: regress-logs-${{ matrix.name }}
path: regress/logs/*.log
if-no-files-found: ignore

1
.gitignore vendored
View File

@@ -21,6 +21,7 @@ configure
core
etc/
fuzz/*-fuzzer
regress/logs/
tags
tmux
tmux.1.*

View File

@@ -51,10 +51,9 @@ cmd_break_pane_float(struct cmdq_item *item, struct args *args,
struct window *w, struct window_pane *wp)
{
struct layout_cell *lc = wp->layout_cell;
u_int sx = lc->saved_sx, sy = lc->saved_sy;
int ox = lc->saved_xoff, oy = lc->saved_yoff;
char *cause = NULL;
enum pane_lines lines = window_get_pane_lines(w);
struct layout_geometry *fg = &lc->fg;
if (window_pane_is_floating(wp)) {
cmdq_error(item, "pane is already floating");
@@ -65,14 +64,13 @@ cmd_break_pane_float(struct cmdq_item *item, struct args *args,
return (CMD_RETURN_ERROR);
}
if (layout_floating_args_parse(item, args, lines, w, &sx, &sy, &ox, &oy,
&cause) != 0) {
if (layout_floating_args_parse(item, args, lines, w, fg, &cause) != 0) {
cmdq_error(item, "failed to float pane: %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
layout_remove_tile(w, lc);
layout_set_size(lc, sx, sy, ox, oy);
layout_set_size(lc, fg->sx, fg->sy, fg->xoff, fg->yoff);
lc->flags |= LAYOUT_CELL_FLOATING;
TAILQ_REMOVE(&w->z_index, wp, zentry);

View File

@@ -126,6 +126,10 @@ cmd_display_menu_get_pos(struct client *tc, struct cmdq_item *item,
format_add(ft, "popup_mouse_y", "%u", event->m.y);
}
/* Position of the previous menu, for -x/-y L. */
format_add(ft, "popup_last_x", "%u", tc->menu_last_px);
format_add(ft, "popup_last_y", "%u", tc->menu_last_py + h);
/*
* If there are any status lines, add this window position and the
* status line position.
@@ -238,6 +242,8 @@ cmd_display_menu_get_pos(struct client *tc, struct cmdq_item *item,
xp = "#{popup_pane_left}";
else if (strcmp(xp, "M") == 0)
xp = "#{popup_mouse_centre_x}";
else if (strcmp(xp, "L") == 0)
xp = "#{popup_last_x}";
else if (strcmp(xp, "W") == 0)
xp = "#{popup_window_status_line_x}";
p = format_expand(ft, xp);
@@ -258,6 +264,8 @@ cmd_display_menu_get_pos(struct client *tc, struct cmdq_item *item,
yp = "#{popup_pane_bottom}";
else if (strcmp(yp, "M") == 0)
yp = "#{popup_mouse_top}";
else if (strcmp(yp, "L") == 0)
yp = "#{popup_last_y}";
else if (strcmp(yp, "S") == 0)
yp = "#{popup_status_line_y}";
else if (strcmp(yp, "W") == 0)

View File

@@ -71,8 +71,9 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
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 wx = w->sx, wy = w->sy;
int px = lc->g.sx, py = lc->g.sy;
int xoff = lc->g.xoff, yoff = lc->g.yoff;
int border = 1;
if (window_pane_get_pane_lines(wp) == PANE_LINES_NONE)
@@ -180,9 +181,9 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
return (CMD_RETURN_ERROR);
}
if (xoff != lc->xoff || yoff != lc->yoff) {
lc->xoff = xoff;
lc->yoff = yoff;
if (xoff != lc->g.xoff || yoff != lc->g.yoff) {
lc->g.xoff = xoff;
lc->g.yoff = yoff;
layout_fix_panes(w, NULL);
}
notify_window("window-layout-changed", w);
@@ -200,7 +201,7 @@ cmd_join_pane_move(struct cmdq_item *item, struct args *args,
const char *errstr, *argval;
const char flags[] = { 'U', 'D', 'L', 'R' };
char *cause = NULL, flag;
int xoff = lc->xoff, yoff = lc->yoff, adjust;
int xoff = lc->g.xoff, yoff = lc->g.yoff, adjust;
u_int i;
enum pane_lines lines = window_pane_get_pane_lines(wp);
@@ -251,9 +252,9 @@ cmd_join_pane_move(struct cmdq_item *item, struct args *args,
xoff += adjust;
}
if (xoff != lc->xoff || yoff != lc->yoff) {
lc->xoff = xoff;
lc->yoff = yoff;
if (xoff != lc->g.xoff || yoff != lc->g.yoff) {
lc->g.xoff = xoff;
lc->g.yoff = yoff;
layout_fix_panes(w, NULL);
notify_window("window-layout-changed", w);
server_redraw_window(w);
@@ -319,8 +320,8 @@ cmd_join_pane_mouse_move(struct client *c, struct mouse_event *m)
ly = m->statusat - 1;
if (x != lx || y != ly) {
lc->xoff += x - lx;
lc->yoff += y - ly;
lc->g.xoff += x - lx;
lc->g.yoff += y - ly;
layout_fix_panes(w, NULL);
server_redraw_window(w);
server_redraw_window_borders(w);
@@ -378,10 +379,11 @@ cmd_join_pane_tile(struct cmdq_item *item, struct args *args, struct window *w,
return (CMD_RETURN_ERROR);
}
lc->saved_sx = lc->sx;
lc->saved_sy = lc->sy;
lc->saved_xoff = lc->xoff;
lc->saved_yoff = lc->yoff;
lc->fg.sx = lc->g.sx;
lc->fg.sy = lc->g.sy;
lc->fg.xoff = lc->g.xoff;
lc->fg.yoff = lc->g.yoff;
if (layout_insert_tile(w, lc) != 0) {
cmdq_error(item, "no space for a new pane");
return (CMD_RETURN_ERROR);

View File

@@ -270,10 +270,10 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
if ((lx == left || lx == left + 1) && ly == wp->yoff - 1) {
/* Top left corner. */
new_sx = lc->sx + (lx - x);
new_sx = lc->g.sx + (lx - x);
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = lc->sy + (ly - y);
new_sy = lc->g.sy + (ly - y);
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
new_xoff = x + 1; /* because mouse is on border at xoff - 1 */
@@ -283,65 +283,65 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
} else if ((lx == right + 1 || lx == right) &&
ly == wp->yoff - 1) {
/* Top right corner. */
new_sx = x - lc->xoff;
new_sx = x - lc->g.xoff;
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = lc->sy + (ly - y);
new_sy = lc->g.sy + (ly - y);
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
new_yoff = y + 1;
layout_set_size(lc, new_sx, new_sy, lc->xoff, new_yoff);
layout_set_size(lc, new_sx, new_sy, lc->g.xoff, new_yoff);
resizes++;
} else if ((lx == left || lx == left + 1) &&
ly == wp->yoff + sy) {
/* Bottom left corner. */
new_sx = lc->sx + (lx - x);
new_sx = lc->g.sx + (lx - x);
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = y - lc->yoff;
new_sy = y - lc->g.yoff;
if (new_sy < PANE_MINIMUM)
return;
new_xoff = x + 1;
layout_set_size(lc, new_sx, new_sy, new_xoff, lc->yoff);
layout_set_size(lc, new_sx, new_sy, new_xoff, lc->g.yoff);
resizes++;
} else if ((lx == right + 1 || lx == right) &&
ly == wp->yoff + sy) {
/* Bottom right corner. */
new_sx = x - lc->xoff;
new_sx = x - lc->g.xoff;
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = y - lc->yoff;
new_sy = y - lc->g.yoff;
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
layout_set_size(lc, new_sx, new_sy, lc->xoff, lc->yoff);
layout_set_size(lc, new_sx, new_sy, lc->g.xoff, lc->g.yoff);
resizes++;
} else if (lx == right) {
/* Right border. */
new_sx = x - lc->xoff;
new_sx = x - lc->g.xoff;
if (new_sx < PANE_MINIMUM)
return;
layout_set_size(lc, new_sx, lc->sy, lc->xoff, lc->yoff);
layout_set_size(lc, new_sx, lc->g.sy, lc->g.xoff, lc->g.yoff);
resizes++;
} else if (lx == left) {
/* Left border. */
new_sx = lc->sx + (lx - x);
new_sx = lc->g.sx + (lx - x);
if (new_sx < PANE_MINIMUM)
return;
new_xoff = x + 1;
layout_set_size(lc, new_sx, lc->sy, new_xoff, lc->yoff);
layout_set_size(lc, new_sx, lc->g.sy, new_xoff, lc->g.yoff);
resizes++;
} else if (ly == wp->yoff + sy) {
/* Bottom border. */
new_sy = y - lc->yoff;
new_sy = y - lc->g.yoff;
if (new_sy < PANE_MINIMUM)
return;
layout_set_size(lc, lc->sx, new_sy, lc->xoff, lc->yoff);
layout_set_size(lc, lc->g.sx, new_sy, lc->g.xoff, lc->g.yoff);
resizes++;
} else if (ly == wp->yoff - 1) {
/* Top border (move instead of resize). */
new_xoff = lc->xoff + (x - lx);
new_xoff = lc->g.xoff + (x - lx);
new_yoff = y + 1;
layout_set_size(lc, lc->sx, lc->sy, new_xoff, new_yoff);
layout_set_size(lc, lc->g.sx, lc->g.sy, new_xoff, new_yoff);
resizes++;
}
if (resizes != 0) {

View File

@@ -178,10 +178,10 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *loop;
struct options *oo;
struct options_entry *parent, *o, *po;
char *name, *argument, *expanded = NULL;
char *cause;
char *name, *argument, *cause;
char *expanded = NULL, *array_key = NULL;
const char *value;
int window, idx, already, error, ambiguous;
int window, already, error, ambiguous;
int scope;
window = (cmd_get_entry(self) == &cmd_set_window_option_entry);
@@ -202,8 +202,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
/* Parse option name and index. */
name = options_match(argument, &idx, &ambiguous);
/* Parse option name and array key. */
name = options_match(argument, &array_key, &ambiguous);
if (name == NULL) {
if (args_has(args, 'q'))
goto out;
@@ -235,21 +235,23 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
o = options_get_only(oo, name);
parent = options_get(oo, name);
/* Check that array options and indexes match up. */
if (idx != -1 && (*name == '@' || !options_is_array(parent))) {
/* Check that array options and keys match up. */
if (array_key != NULL && (*name == '@' || !options_is_array(parent))) {
cmdq_error(item, "not an array: %s", argument);
goto fail;
}
/* With -o, check this option is not already set. */
if (!args_has(args, 'u') && args_has(args, 'o')) {
if (idx == -1)
if (array_key == NULL)
already = (o != NULL);
else {
if (o == NULL)
already = 0;
else if (options_array_get(o, array_key) != NULL)
already = 1;
else
already = (options_array_get(o, idx) != NULL);
already = 0;
}
if (already) {
if (args_has(args, 'q'))
@@ -265,7 +267,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
po = options_get_only(loop->options, name);
if (po == NULL)
continue;
if (options_remove_or_default(po, idx, &cause) != 0) {
if (options_remove_or_default(po, array_key,
&cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
@@ -275,7 +278,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'u') || args_has(args, 'U')) {
if (o == NULL)
goto out;
if (options_remove_or_default(o, idx, &cause) != 0) {
if (options_remove_or_default(o, array_key, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
@@ -286,7 +289,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
goto fail;
}
options_set_string(oo, name, append, "%s", value);
} else if (idx == -1 && !options_is_array(parent)) {
} else if (array_key == NULL && !options_is_array(parent)) {
error = options_from_string(oo, options_table_entry(parent),
options_table_entry(parent)->name, value,
args_has(args, 'a'), &cause);
@@ -302,7 +305,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
}
if (o == NULL)
o = options_empty(oo, options_table_entry(parent));
if (idx == -1) {
if (array_key == NULL) {
if (!append)
options_array_clear(o);
if (options_array_assign(o, value, &cause) != 0) {
@@ -310,7 +313,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
free(cause);
goto fail;
}
} else if (options_array_set(o, idx, value, append,
} else if (options_array_set(o, array_key, value, append,
&cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
@@ -324,11 +327,13 @@ out:
free(argument);
free(expanded);
free(name);
free(array_key);
return (CMD_RETURN_NORMAL);
fail:
free(argument);
free(expanded);
free(name);
free(array_key);
return (CMD_RETURN_ERROR);
}

View File

@@ -30,7 +30,7 @@
static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *);
static void cmd_show_options_print(struct cmd *, struct cmdq_item *,
struct options_entry *, int, int);
struct options_entry *, const char *, int);
static void cmd_show_hooks_print_monitor(struct cmdq_item *,
struct options_entry *);
static enum cmd_retval cmd_show_hooks_monitor(struct cmd *, struct cmdq_item *,
@@ -84,7 +84,8 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *target = cmdq_get_target(item);
struct options *oo;
char *argument, *name = NULL, *cause;
int window, idx, ambiguous, parent, scope;
char *array_key = NULL;
int window, ambiguous, parent, scope;
struct options_entry *o;
window = (cmd_get_entry(self) == &cmd_show_window_options_entry);
@@ -106,7 +107,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
}
argument = format_single_from_target(item, args_string(args, 0));
name = options_match(argument, &idx, &ambiguous);
name = options_match(argument, &array_key, &ambiguous);
if (name == NULL) {
if (args_has(args, 'q'))
goto out;
@@ -136,7 +137,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
args_has(args, 'B'))
cmd_show_hooks_print_monitor(item, o);
else
cmd_show_options_print(self, item, o, idx, parent);
cmd_show_options_print(self, item, o, array_key, parent);
}
else if (*name == '@') {
if (args_has(args, 'q'))
@@ -147,26 +148,28 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
out:
free(name);
free(array_key);
free(argument);
return (CMD_RETURN_NORMAL);
fail:
free(name);
free(array_key);
free(argument);
return (CMD_RETURN_ERROR);
}
static void
cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
struct options_entry *o, int idx, int parent)
struct options_entry *o, const char *array_key, int parent)
{
struct args *args = cmd_get_args(self);
struct options_array_item *a;
const char *name = options_name(o);
char *value, *tmp = NULL, *escaped;
if (idx != -1) {
xasprintf(&tmp, "%s[%d]", name, idx);
if (array_key != NULL) {
xasprintf(&tmp, "%s[%s]", name, array_key);
name = tmp;
} else {
if (options_is_array(o)) {
@@ -177,8 +180,8 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
return;
}
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx,
array_key = options_array_item_key(a);
cmd_show_options_print(self, item, o, array_key,
parent);
a = options_array_next(a);
}
@@ -186,7 +189,7 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
}
}
value = options_to_string(o, idx, 0);
value = options_to_string(o, array_key, 0);
if (args_has(args, 'v'))
cmdq_print(item, "%s", value);
else if (options_is_string(o)) {
@@ -242,15 +245,14 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
const struct options_table_entry *oe;
struct options_entry *o;
struct options_array_item *a;
const char *name;
u_int idx;
const char *name, *array_key;
int parent;
if (cmd_get_entry(self) != &cmd_show_hooks_entry) {
o = options_first(oo);
while (o != NULL) {
if (options_table_entry(o) == NULL)
cmd_show_options_print(self, item, o, -1, 0);
cmd_show_options_print(self, item, o, NULL, 0);
o = options_next(o);
}
}
@@ -277,7 +279,7 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
parent = 0;
if (!options_is_array(o))
cmd_show_options_print(self, item, o, -1, parent);
cmd_show_options_print(self, item, o, NULL, parent);
else if ((a = options_array_first(o)) == NULL) {
if (!args_has(args, 'v')) {
name = options_name(o);
@@ -288,8 +290,8 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
}
} else {
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx,
array_key = options_array_item_key(a);
cmd_show_options_print(self, item, o, array_key,
parent);
a = options_array_next(a);
}

View File

@@ -1294,7 +1294,8 @@ colour_palette_from_option(struct colour_palette *p, struct options *oo)
{
struct options_entry *o;
struct options_array_item *a;
u_int i, n;
union options_value *ov;
u_int i;
int c;
if (p == NULL)
@@ -1312,12 +1313,11 @@ colour_palette_from_option(struct colour_palette *p, struct options *oo)
p->default_palette = xcalloc(256, sizeof *p->default_palette);
for (i = 0; i < 256; i++)
p->default_palette[i] = -1;
while (a != NULL) {
n = options_array_item_index(a);
if (n < 256) {
c = options_array_item_value(a)->number;
p->default_palette[n] = c;
for (i = 0; i < 256; i++) {
ov = options_array_getv(o, "%u", i);
if (ov != NULL) {
c = ov->number;
p->default_palette[i] = c;
}
a = options_array_next(a);
}
}

View File

@@ -4241,25 +4241,26 @@ format_find(struct format_tree *ft, const char *key, uint64_t modifiers,
struct format_entry *fe, fe_find;
struct environ_entry *envent;
struct options_entry *o;
int idx;
char *found = NULL, *saved, s[512];
char *array_key = NULL;
const char *errstr;
time_t t = 0;
struct tm tm;
o = options_parse_get(global_options, key, &idx, 0);
o = options_parse_get(global_options, key, &array_key, 0);
if (o == NULL && ft->wp != NULL)
o = options_parse_get(ft->wp->options, key, &idx, 0);
o = options_parse_get(ft->wp->options, key, &array_key, 0);
if (o == NULL && ft->w != NULL)
o = options_parse_get(ft->w->options, key, &idx, 0);
o = options_parse_get(ft->w->options, key, &array_key, 0);
if (o == NULL)
o = options_parse_get(global_w_options, key, &idx, 0);
o = options_parse_get(global_w_options, key, &array_key, 0);
if (o == NULL && ft->s != NULL)
o = options_parse_get(ft->s->options, key, &idx, 0);
o = options_parse_get(ft->s->options, key, &array_key, 0);
if (o == NULL)
o = options_parse_get(global_s_options, key, &idx, 0);
o = options_parse_get(global_s_options, key, &array_key, 0);
if (o != NULL) {
found = options_to_string(o, idx, 1);
found = options_to_string(o, array_key, 1);
free(array_key);
goto found;
}
@@ -4953,7 +4954,7 @@ format_add_window_neighbour(struct format_tree *nft, struct winlink *wl,
oname = options_name(o);
if (*oname == '@') {
xasprintf(&prefixed, "%s_%s", prefix, oname);
oval = options_to_string(o, -1, 1);
oval = options_to_string(o, NULL, 1);
format_add(nft, prefixed, "%s", oval);
free(oval);
free(prefixed);
@@ -5125,11 +5126,12 @@ format_loop_add_option(struct format_expand_state *es, const char *fmt,
nft = format_create(ft->client, ft->item, FORMAT_NONE, ft->flags);
format_add(nft, "option_name", "%s", name);
s = options_to_string(o, -1, 0);
s = options_to_string(o, NULL, 0);
format_add(nft, "option_value", "%s", s);
free(s);
format_add(nft, "option_is_array", "%d", is_array);
format_add(nft, "option_array_key", "%s", "");
format_add(nft, "option_array_index", "%s", "");
format_add(nft, "option_array_first", "%d", is_array);
format_add(nft, "option_array_last", "%d", is_array);
@@ -5167,20 +5169,21 @@ format_loop_add_array_item(struct format_expand_state *es, const char *fmt,
struct format_expand_state next;
const struct options_table_entry *oe = options_table_entry(o);
const char *name = options_name(o);
const char *array_key;
char *expanded, *s;
u_int idx;
idx = options_array_item_index(a);
format_log(es, "option loop: %s[%u]", name, idx);
array_key = options_array_item_key(a);
format_log(es, "option loop: %s[%s]", name, array_key);
nft = format_create(ft->client, ft->item, FORMAT_NONE, ft->flags);
format_add(nft, "option_name", "%s", name);
s = options_to_string(o, idx, 0);
s = options_to_string(o, array_key, 0);
format_add(nft, "option_value", "%s", s);
free(s);
format_add(nft, "option_is_array", "1");
format_add(nft, "option_array_index", "%u", idx);
format_add(nft, "option_array_key", "%s", array_key);
format_add(nft, "option_array_index", "%s", array_key);
if (a == options_array_first(o))
format_add(nft, "option_array_first", "1");
else

View File

@@ -2818,7 +2818,7 @@ input_exit_rename(struct input_ctx *ictx)
if (ictx->input_len == 0) {
o = options_get_only(w->options, "automatic-rename");
if (o != NULL)
options_remove_or_default(o, -1, NULL);
options_remove_or_default(o, NULL, NULL);
if (!options_get_number(w->options, "automatic-rename"))
window_set_name(w, "", 1);
} else {

View File

@@ -59,6 +59,8 @@
" '#{?mouse_hyperlink,Type #[underscore]#{=/9/...:mouse_hyperlink},}' 'C-h' {copy-mode -q; send-keys -l -- \"#{q:mouse_hyperlink}\"}" \
" '#{?mouse_hyperlink,Copy #[underscore]#{=/9/...:mouse_hyperlink},}' 'h' {copy-mode -q; set-buffer -- \"#{q:mouse_hyperlink}\"}" \
" ''" \
" '#{?#{#{pane_floating_flag}},Move,}' '' {display-menu -xL -yL -T '#[align=centre]Move' " DEFAULT_MOVE_MENU " }" \
" '#{?#{#{pane_floating_flag}},Move & Resize,}' '' {display-menu -xL -yL -T '#[align=centre]Move & Resize' " DEFAULT_MOVE_RESIZE_MENU " }" \
" '#{?#{#{pane_floating_flag}},Tile,}' 't' { join-pane }" \
" '#{?#{!:#{pane_floating_flag}},Float,}' 'f' { break-pane -W }" \
" '#{?#{!:#{pane_floating_flag}},Horizontal Split,}' 'h' {split-window -h}" \
@@ -72,6 +74,30 @@
" 'Respawn' 'R' {respawn-pane -k}" \
" '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \
" '#{?#{>:#{window_panes},1},,-}#{?window_zoomed_flag,Unzoom,Zoom}' 'z' {resize-pane -Z}"
#define DEFAULT_MOVE_MENU \
" 'Centre' 'c' {move-pane -P centre}" \
" ''" \
" 'Top Left' '1' {move-pane -P top-left}" \
" 'Top Right' '2' {move-pane -P top-right}" \
" 'Bottom Left' '3' {move-pane -P bottom-left}" \
" 'Bottom Right' '4' {move-pane -P bottom-right}" \
" ''" \
" 'Top' 't' {move-pane -P top-centre}" \
" 'Bottom' 'b' {move-pane -P bottom-centre}" \
" 'Left' 'l' {move-pane -P centre-left}" \
" 'Right' 'r' {move-pane -P centre-right}"
#define DEFAULT_MOVE_RESIZE_MENU \
" 'Fill' '0' {resize-pane -x100% -y100%; move-pane -P top-left}" \
" ''" \
" 'Top Left' '1' {resize-pane -x50% -y50%; move-pane -P top-left}" \
" 'Top Right' '2' {resize-pane -x50% -y50%; move-pane -P top-right}" \
" 'Bottom Left' '3' {resize-pane -x50% -y50%; move-pane -P bottom-left}" \
" 'Bottom Right' '4' {resize-pane -x50% -y50%; move-pane -P bottom-right}" \
" ''" \
" 'Top' 't' {resize-pane -x100% -y50%; move-pane -P top-centre}" \
" 'Bottom' 'b' {resize-pane -x100% -y50%; move-pane -P bottom-centre}" \
" 'Left' 'l' {resize-pane -x50% -y100%; move-pane -P centre-left}" \
" 'Right' 'r' {resize-pane -x50% -y100%; move-pane -P centre-right}"
static int key_bindings_cmp(struct key_binding *, struct key_binding *);
RB_GENERATE_STATIC(key_bindings, key_binding, entry, key_bindings_cmp);
@@ -362,14 +388,14 @@ key_bindings_init(void)
"bind -N 'Rename current session' '$' { command-prompt -I'#S' { rename-session -- '%%' } }",
"bind -N 'Split window horizontally' % { split-window -h }",
"bind -N 'Kill current window' & { confirm-before -p\"kill-window #W? (y/n)\" kill-window }",
"bind -N 'Prompt for window index to select' \"'\" { command-prompt -T window-target -pindex { select-window -t ':%%' } }",
"bind -N 'Prompt for window index to select' \"'\" { command-prompt -pindex { select-window -t ':%%' } }",
"bind -N 'New floating pane' * { new-pane }",
"bind -N 'Toggle pane between floating and tiled' @ { if -F '#{pane_floating_flag}' { join-pane } { break-pane -W } }",
"bind -N 'Switch to previous client' ( { switch-client -p }",
"bind -N 'Switch to next client' ) { switch-client -n }",
"bind -N 'Rename current window' , { command-prompt -I'#W' { rename-window -- '%%' } }",
"bind -N 'Delete the most recent paste buffer' - { delete-buffer }",
"bind -N 'Move the current window' . { command-prompt -T target { move-window -t '%%' } }",
"bind -N 'Move the current window' . { command-prompt { move-window -t '%%' } }",
"bind -N 'Describe key binding' '/' { command-prompt -kpkey { list-keys -1N '%%' } }",
"bind -N 'Select window 0' 0 { select-window -t:=0 }",
"bind -N 'Select window 1' 1 { select-window -t:=1 }",
@@ -410,10 +436,8 @@ key_bindings_init(void)
"bind -N 'Choose a window from a list' w { choose-tree -Zw }",
"bind -N 'Kill the active pane' x { confirm-before -p\"kill-pane #P? (y/n)\" kill-pane }",
"bind -N 'Zoom the active pane' z { resize-pane -Z }",
"bind -N 'Move pane to top-left corner' '{' { resize-pane -x50% -y50%; move-pane -P top-left }",
"bind -N 'Move pane to top-right corner' '}' { resize-pane -x50% -y50%; move-pane -P top-right }",
"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 'Swap the active pane with the pane above' '{' { swap-pane -U }",
"bind -N 'Swap the active pane with the pane below' '}' { swap-pane -D }",
"bind -N 'Show messages' '~' { show-messages }",
"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 }",
@@ -444,6 +468,28 @@ key_bindings_init(void)
"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 right' -r C-Right { resize-pane -R }",
/* Floating pane movement. */
"bind -N 'Move a floating pane' g { switch-client -Tmove }",
"bind -Tmove -N 'Move pane to top-left corner' 1 { move-pane -P top-left }",
"bind -Tmove -N 'Move pane to top-right corner' 2 { move-pane -P top-right }",
"bind -Tmove -N 'Move pane to bottom-left corner' 3 { move-pane -P bottom-left }",
"bind -Tmove -N 'Move pane to bottom-right corner' 4 { move-pane -P bottom-right }",
"bind -Tmove -N 'Move pane to top-left corner and resize' M-1 { resize-pane -x50% -y50%; move-pane -P top-left }",
"bind -Tmove -N 'Move pane to top-right corner and resize' M-2 { resize-pane -x50% -y50%; move-pane -P top-right }",
"bind -Tmove -N 'Move pane to bottom-left corner and resize' M-3 { resize-pane -x50% -y50%; move-pane -P bottom-left }",
"bind -Tmove -N 'Move pane to bottom-right corner and resize' M-4 { resize-pane -x50% -y50%; move-pane -P bottom-right }",
"bind -Tmove -N 'Move pane to top' 'Up' { move-pane -P top-centre }",
"bind -Tmove -N 'Move pane to bottom' 'Down' { move-pane -P bottom-centre }",
"bind -Tmove -N 'Move pane to left' 'Left' { move-pane -P centre-left }",
"bind -Tmove -N 'Move pane to right' 'Right' { move-pane -P centre-right }",
"bind -Tmove -N 'Move pane to top and resize' 'M-Up' { resizep -x100% -y50%; move-pane -P top-centre }",
"bind -Tmove -N 'Move pane to bottom and resize' 'M-Down' { resizep -x100% -y50%; move-pane -P bottom-centre }",
"bind -Tmove -N 'Move pane to left and resize' 'M-Left' { resizep -x50% -y100%; move-pane -P centre-left }",
"bind -Tmove -N 'Move pane to right and resize' 'M-Right' { resizep -x50% -y100%; move-pane -P centre-right }",
"bind -Tmove -N 'Move pane to fill the window' 0 { resize-pane -x100% -y100%; move-pane -P top-left }",
"bind -Tmove -N 'Display move menu' , { if -F '#{pane_floating_flag}' { display-menu -xP -yP -T '#[align=centre]Move' " DEFAULT_MOVE_MENU " } }",
"bind -Tmove -N 'Display move and resize menu' . { if -F '#{pane_floating_flag}' { display-menu -xP -yP -T '#[align=centre]Move & Resize' " DEFAULT_MOVE_RESIZE_MENU " } }",
/* Menu keys */
"bind -N 'Display window menu' < { display-menu -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU " }",
"bind -N 'Display pane menu' > { display-menu -xP -yP -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }",

View File

@@ -231,19 +231,19 @@ layout_append_legacy(struct layout_cell *lc, char *buf, size_t len)
if (lc == NULL)
return (0);
sx = lc->sx;
sy = lc->sy;
xoff = lc->xoff;
yoff = lc->yoff;
sx = lc->g.sx;
sy = lc->g.sy;
xoff = lc->g.xoff;
yoff = lc->g.yoff;
if (lc->flags & LAYOUT_CELL_HIDDEN) {
if (lc->saved_sx != UINT_MAX)
sx = lc->saved_sx;
if (lc->saved_sy != UINT_MAX)
sy = lc->saved_sy;
if (lc->saved_xoff != INT_MAX)
xoff = lc->saved_xoff;
if (lc->saved_yoff != INT_MAX)
yoff = lc->saved_yoff;
if (lc->fg.sx != UINT_MAX)
sx = lc->fg.sx;
if (lc->fg.sy != UINT_MAX)
sy = lc->fg.sy;
if (lc->fg.xoff != INT_MAX)
xoff = lc->fg.xoff;
if (lc->fg.yoff != INT_MAX)
yoff = lc->fg.yoff;
}
if (lc->wp != NULL) {
@@ -293,19 +293,19 @@ layout_append(struct window *w, struct layout_cell *lc, char *buf, size_t len)
if (lc == NULL)
return (0);
sx = lc->sx;
sy = lc->sy;
xoff = lc->xoff;
yoff = lc->yoff;
sx = lc->g.sx;
sy = lc->g.sy;
xoff = lc->g.xoff;
yoff = lc->g.yoff;
if (lc->flags & LAYOUT_CELL_HIDDEN) {
if (lc->saved_sx != UINT_MAX)
sx = lc->saved_sx;
if (lc->saved_sy != UINT_MAX)
sy = lc->saved_sy;
if (lc->saved_xoff != INT_MAX)
xoff = lc->saved_xoff;
if (lc->saved_yoff != INT_MAX)
yoff = lc->saved_yoff;
if (lc->fg.sx != UINT_MAX)
sx = lc->fg.sx;
if (lc->fg.sy != UINT_MAX)
sy = lc->fg.sy;
if (lc->fg.xoff != INT_MAX)
xoff = lc->fg.xoff;
if (lc->fg.yoff != INT_MAX)
yoff = lc->fg.yoff;
}
if (lc->wp != NULL) {
@@ -375,24 +375,24 @@ layout_check(struct layout_cell *lc)
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (!layout_has_tiled(lcchild))
continue;
if (lcchild->sy != lc->sy || !layout_check(lcchild))
if (lcchild->g.sy != lc->g.sy || !layout_check(lcchild))
return (0);
n += lcchild->sx + 1;
n += lcchild->g.sx + 1;
children++;
}
if (children != 0 && n - 1 != lc->sx)
if (children != 0 && n - 1 != lc->g.sx)
return (0);
break;
case LAYOUT_TOPBOTTOM:
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (!layout_has_tiled(lcchild))
continue;
if (lcchild->sx != lc->sx || !layout_check(lcchild))
if (lcchild->g.sx != lc->g.sx || !layout_check(lcchild))
return (0);
n += lcchild->sy + 1;
n += lcchild->g.sy + 1;
children++;
}
if (children != 0 && n - 1 != lc->sy)
if (children != 0 && n - 1 != lc->g.sy)
return (0);
break;
}
@@ -423,21 +423,21 @@ layout_fix_legacy_root(struct layout_cell *root)
return (0);
if (root->type == LAYOUT_LEFTRIGHT) {
TAILQ_FOREACH(lcchild, &root->cells, entry) {
sy = lcchild->sy + 1;
sx += lcchild->sx + 1;
sy = lcchild->g.sy + 1;
sx += lcchild->g.sx + 1;
}
} else {
TAILQ_FOREACH(lcchild, &root->cells, entry) {
sx = lcchild->sx + 1;
sy += lcchild->sy + 1;
sx = lcchild->g.sx + 1;
sy += lcchild->g.sy + 1;
}
}
if (sx != 0 && sy != 0 &&
(root->sx != sx - 1 || root->sy != sy - 1)) {
(root->g.sx != sx - 1 || root->g.sy != sy - 1)) {
if (sx - 1 > PANE_MAXIMUM || sy - 1 > PANE_MAXIMUM)
return (-1);
root->sx = sx - 1;
root->sy = sy - 1;
root->g.sx = sx - 1;
root->g.sy = sy - 1;
}
return (0);
}
@@ -736,10 +736,10 @@ layout_construct(struct layout_parse_ctx *ctx, struct layout_cell *parent,
(lc->flags & LAYOUT_CELL_FLOATING) == 0)
goto fail;
if (lc->flags & LAYOUT_CELL_HIDDEN) {
lc->saved_sx = lc->sx;
lc->saved_sy = lc->sy;
lc->saved_xoff = lc->xoff;
lc->saved_yoff = lc->yoff;
lc->fg.sx = lc->g.sx;
lc->fg.sy = lc->g.sy;
lc->fg.xoff = lc->g.xoff;
lc->fg.yoff = lc->g.yoff;
}
} else if (ctx->ptr != ctx->end &&
(*ctx->ptr == '{' || *ctx->ptr == '[')) {
@@ -815,26 +815,26 @@ layout_resolve_relative(struct layout_cell *lc, u_int sx, u_int sy)
return (0);
}
if (lc->flags & LAYOUT_CELL_X_RELATIVE) {
resolved = (long long)sx - lc->sx - lc->xoff;
resolved = (long long)sx - lc->g.sx - lc->g.xoff;
if (resolved < INT_MIN || resolved > INT_MAX)
return (-1);
lc->xoff = resolved;
lc->g.xoff = resolved;
lc->flags &= ~LAYOUT_CELL_X_RELATIVE;
}
if (lc->flags & LAYOUT_CELL_Y_RELATIVE) {
resolved = (long long)sy - lc->sy - lc->yoff;
resolved = (long long)sy - lc->g.sy - lc->g.yoff;
if (resolved < INT_MIN || resolved > INT_MAX)
return (-1);
lc->yoff = resolved;
lc->g.yoff = resolved;
lc->flags &= ~LAYOUT_CELL_Y_RELATIVE;
}
if (lc->flags & LAYOUT_CELL_HIDDEN) {
lc->saved_sx = lc->sx;
lc->saved_sy = lc->sy;
lc->saved_xoff = lc->xoff;
lc->saved_yoff = lc->yoff;
lc->fg.sx = lc->g.sx;
lc->fg.sy = lc->g.sy;
lc->fg.xoff = lc->g.xoff;
lc->fg.yoff = lc->g.yoff;
}
if (!layout_check_geometry(lc->sx, lc->sy, lc->xoff, lc->yoff))
if (!layout_check_geometry(lc->g.sx, lc->g.sy, lc->g.xoff, lc->g.yoff))
return (-1);
return (0);
}
@@ -1009,8 +1009,8 @@ layout_prepare(struct window *w, const char *layout, char **cause)
return (NULL);
}
if (layout_resolve_relative(root,
root->type == LAYOUT_WINDOWPANE ? w->sx : root->sx,
root->type == LAYOUT_WINDOWPANE ? w->sy : root->sy) != 0) {
root->type == LAYOUT_WINDOWPANE ? w->sx : root->g.sx,
root->type == LAYOUT_WINDOWPANE ? w->sy : root->g.sy) != 0) {
*cause = xstrdup("invalid layout");
layout_free_cell(root, 0);
return (NULL);
@@ -1127,7 +1127,7 @@ layout_apply_prepared(struct window *w, struct layout_prepared *prepared)
layout_fix_zindexes(w, root);
if (layout_has_tiled(root))
window_resize(w, root->sx, root->sy, -1, -1);
window_resize(w, root->g.sx, root->g.sy, -1, -1);
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
layout_print_cell(root, __func__, 0);

View File

@@ -170,8 +170,8 @@ layout_set_even(struct window *w, enum layout_type type)
TAILQ_INSERT_TAIL(&lcroot->cells, lcchild, entry);
lcchild->parent = lcroot;
if (layout_cell_is_tiled(lcchild)) {
lcchild->sx = w->sx;
lcchild->sy = w->sy;
lcchild->g.sx = w->sx;
lcchild->g.sy = w->sy;
}
}
@@ -182,7 +182,7 @@ layout_set_even(struct window *w, enum layout_type type)
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -292,7 +292,7 @@ layout_set_main_h(struct window *w)
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -390,7 +390,7 @@ layout_set_main_h_mirrored(struct window *w)
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -488,7 +488,7 @@ layout_set_main_v(struct window *w)
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -587,7 +587,7 @@ layout_set_main_v_mirrored(struct window *w)
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -709,7 +709,7 @@ layout_set_tiled(struct window *w)
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}

290
layout.c
View File

@@ -56,34 +56,31 @@ static int layout_set_size_check(struct window *, struct layout_cell *,
static void layout_resize_child_cells(struct window *,
struct layout_cell *);
/* Initializes cell geometry to sentinel values. */
static void
layout_geometry_init(struct layout_geometry *lg)
{
lg->sx = UINT_MAX;
lg->sy = UINT_MAX;
lg->xoff = INT_MAX;
lg->yoff = INT_MAX;
}
/* Create a new layout cell. */
struct layout_cell *
layout_create_cell(struct layout_cell *lcparent)
{
struct layout_cell *lc;
lc = xmalloc(sizeof *lc);
lc = xcalloc(1, sizeof *lc);
lc->type = LAYOUT_WINDOWPANE;
lc->flags = 0;
lc->z_index = UINT_MAX;
lc->pane_id = UINT_MAX;
lc->parent = lcparent;
TAILQ_INIT(&lc->cells);
lc->sx = UINT_MAX;
lc->sy = UINT_MAX;
lc->xoff = INT_MAX;
lc->yoff = INT_MAX;
lc->saved_sx = UINT_MAX;
lc->saved_sy = UINT_MAX;
lc->saved_xoff = INT_MAX;
lc->saved_yoff = INT_MAX;
lc->wp = NULL;
layout_geometry_init(&lc->g);
layout_geometry_init(&lc->fg);
return (lc);
}
@@ -146,8 +143,8 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
break;
}
log_debug("%s:%*s%p type %s [parent %p] wp=%p [%d,%d %ux%u]", hdr, n,
" ", lc, type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx,
lc->sy);
" ", lc, type, lc->parent, lc->wp, lc->g.xoff, lc->g.yoff, lc->g.sx,
lc->g.sy);
switch (lc->type) {
case LAYOUT_LEFTRIGHT:
case LAYOUT_TOPBOTTOM:
@@ -166,10 +163,10 @@ layout_search_by_border(struct layout_cell *lc, u_int x, u_int y)
struct layout_cell *lcchild, *last = NULL;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if ((int)x >= lcchild->xoff &&
(int)x < lcchild->xoff + (int)lcchild->sx &&
(int)y >= lcchild->yoff &&
(int)y < lcchild->yoff + (int)lcchild->sy) {
if ((int)x >= lcchild->g.xoff &&
(int)x < lcchild->g.xoff + (int)lcchild->g.sx &&
(int)y >= lcchild->g.yoff &&
(int)y < lcchild->g.yoff + (int)lcchild->g.sy) {
/* Inside the cell - recurse. */
return (layout_search_by_border(lcchild, x, y));
}
@@ -181,13 +178,13 @@ layout_search_by_border(struct layout_cell *lc, u_int x, u_int y)
switch (lc->type) {
case LAYOUT_LEFTRIGHT:
if ((int)x < lcchild->xoff &&
(int)x >= last->xoff + (int)last->sx)
if ((int)x < lcchild->g.xoff &&
(int)x >= last->g.xoff + (int)last->g.sx)
return (last);
break;
case LAYOUT_TOPBOTTOM:
if ((int)y < lcchild->yoff &&
(int)y >= last->yoff + (int)last->sy)
if ((int)y < lcchild->g.yoff &&
(int)y >= last->g.yoff + (int)last->g.sy)
return (last);
break;
case LAYOUT_WINDOWPANE:
@@ -204,11 +201,11 @@ layout_search_by_border(struct layout_cell *lc, u_int x, u_int y)
void
layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, int xoff, int yoff)
{
lc->sx = sx;
lc->sy = sy;
lc->g.sx = sx;
lc->g.sy = sy;
lc->xoff = xoff;
lc->yoff = yoff;
lc->g.xoff = xoff;
lc->g.yoff = yoff;
}
/* Make a cell a leaf cell. */
@@ -332,26 +329,26 @@ layout_fix_offsets1(struct layout_cell *lc)
int xoff, yoff;
if (lc->type == LAYOUT_LEFTRIGHT) {
xoff = lc->xoff;
xoff = lc->g.xoff;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (!layout_has_tiled(lcchild))
continue;
lcchild->xoff = xoff;
lcchild->yoff = lc->yoff;
lcchild->g.xoff = xoff;
lcchild->g.yoff = lc->g.yoff;
if (lcchild->type != LAYOUT_WINDOWPANE)
layout_fix_offsets1(lcchild);
xoff += lcchild->sx + 1;
xoff += lcchild->g.sx + 1;
}
} else {
yoff = lc->yoff;
yoff = lc->g.yoff;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (!layout_has_tiled(lcchild))
continue;
lcchild->xoff = lc->xoff;
lcchild->yoff = yoff;
lcchild->g.xoff = lc->g.xoff;
lcchild->g.yoff = yoff;
if (lcchild->type != LAYOUT_WINDOWPANE)
layout_fix_offsets1(lcchild);
yoff += lcchild->sy + 1;
yoff += lcchild->g.sy + 1;
}
}
}
@@ -366,8 +363,8 @@ layout_fix_offsets(struct window *w)
if (lc->flags & LAYOUT_CELL_FLOATING)
return;
lc->xoff = 0;
lc->yoff = 0;
lc->g.xoff = 0;
lc->g.yoff = 0;
layout_fix_offsets1(lc);
}
@@ -451,16 +448,17 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
old_sx = wp->sx;
old_sy = wp->sy;
wp->xoff = lc->xoff;
wp->yoff = lc->yoff;
sx = lc->sx;
sy = lc->sy;
wp->xoff = lc->g.xoff;
wp->yoff = lc->g.yoff;
sx = lc->g.sx;
sy = lc->g.sy;
if (!window_pane_is_floating(wp) &&
layout_add_horizontal_border(w, lc, status)) {
if (status == PANE_STATUS_TOP)
wp->yoff++;
sy--;
if (sy > 1)
sy--;
}
if (window_pane_scrollbar_reserve(wp)) {
@@ -534,14 +532,14 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
if (lc->type == LAYOUT_WINDOWPANE) {
/* Space available in this cell only. */
if (type == LAYOUT_LEFTRIGHT) {
available = lc->sx;
available = lc->g.sx;
if (w->sb == PANE_SCROLLBARS_ALWAYS)
minimum = PANE_MINIMUM + sb_style->width +
sb_style->pad;
else
minimum = PANE_MINIMUM;
} else {
available = lc->sy;
available = lc->g.sy;
if (layout_add_horizontal_border(w, lc, status))
minimum = PANE_MINIMUM + 1;
else
@@ -579,12 +577,13 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc,
enum layout_type type, int change)
{
struct layout_cell *lcchild;
int changed;
/* Adjust the cell size. */
if (type == LAYOUT_LEFTRIGHT)
lc->sx += change;
lc->g.sx += change;
else
lc->sy += change;
lc->g.sy += change;
/* If this is a leaf cell, that is all that is necessary. */
if (type == LAYOUT_WINDOWPANE)
@@ -611,6 +610,7 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc,
* until no further change is possible.
*/
while (change != 0) {
changed = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (change == 0)
break;
@@ -619,13 +619,17 @@ layout_resize_adjust(struct window *w, struct layout_cell *lc,
if (change > 0) {
layout_resize_adjust(w, lcchild, type, 1);
change--;
changed = 1;
continue;
}
if (layout_resize_check(w, lcchild, type) > 0) {
layout_resize_adjust(w, lcchild, type, -1);
change++;
changed = 1;
}
}
if (!changed)
break;
}
}
@@ -637,9 +641,9 @@ layout_resize_set_size(struct window *w, struct layout_cell *lc,
int change;
if (type == LAYOUT_LEFTRIGHT)
change = size - lc->sx;
change = size - lc->g.sx;
else
change = size - lc->sy;
change = size - lc->g.sy;
layout_resize_adjust(w, lc, type, change);
}
@@ -711,9 +715,9 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc,
lcother = layout_cell_get_neighbour(lc);
if (lcother != NULL) {
if (lcparent->type == LAYOUT_LEFTRIGHT)
change = lc->sx + 1;
change = lc->g.sx + 1;
else
change = lc->sy + 1;
change = lc->g.sy + 1;
layout_resize_adjust(w, lcother, lcparent->type, change);
} else
layout_remove_tile(w, lcparent);
@@ -734,8 +738,8 @@ out:
lc->parent = lcparent->parent;
if (lc->parent == NULL) {
if (layout_cell_is_tiled(lc)) {
lc->xoff = 0;
lc->yoff = 0;
lc->g.xoff = 0;
lc->g.yoff = 0;
}
*lcroot = lc;
} else
@@ -786,29 +790,29 @@ layout_resize(struct window *w, u_int sx, u_int sy)
*/
if (lc->type == LAYOUT_WINDOWPANE && (lc->flags & LAYOUT_CELL_FLOATING))
return;
xchange = sx - lc->sx;
xchange = sx - lc->g.sx;
xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT);
if (xchange < 0 && xchange < -xlimit)
xchange = -xlimit;
if (xlimit == 0) {
if (sx <= lc->sx) /* lc->sx is minimum possible */
if (sx <= lc->g.sx) /* lc->g.sx is minimum possible */
xchange = 0;
else
xchange = sx - lc->sx;
xchange = sx - lc->g.sx;
}
if (xchange != 0)
layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, xchange);
/* Adjust vertically in a similar fashion. */
ychange = sy - lc->sy;
ychange = sy - lc->g.sy;
ylimit = layout_resize_check(w, lc, LAYOUT_TOPBOTTOM);
if (ychange < 0 && ychange < -ylimit)
ychange = -ylimit;
if (ylimit == 0) {
if (sy <= lc->sy) /* lc->sy is minimum possible */
if (sy <= lc->g.sy) /* lc->g.sy is minimum possible */
ychange = 0;
else
ychange = sy - lc->sy;
ychange = sy - lc->g.sy;
}
if (ychange != 0)
layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, ychange);
@@ -839,9 +843,9 @@ layout_resize_pane_to(struct window_pane *wp, enum layout_type type,
/* Work out the size adjustment. */
if (type == LAYOUT_LEFTRIGHT)
size = lc->sx;
size = lc->g.sx;
else
size = lc->sy;
size = lc->g.sy;
if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
change = size - new_size;
else
@@ -872,13 +876,13 @@ layout_resize_floating_pane_to(struct window_pane *wp, enum layout_type type,
}
if (type == LAYOUT_TOPBOTTOM) {
if (lc->sy == size)
if (lc->g.sy == size)
return (0);
lc->sy = size;
lc->g.sy = size;
} else {
if (lc->sx == size)
if (lc->g.sx == size)
return (0);
lc->sx = size;
lc->g.sx = size;
}
redraw_invalidate_scene(wp->window);
return (0);
@@ -900,23 +904,23 @@ layout_resize_floating_pane(struct window_pane *wp, enum layout_type type,
return (0);
if (type == LAYOUT_TOPBOTTOM) {
size = lc->sy + change;
size = lc->g.sy + change;
if (size < PANE_MINIMUM || size > PANE_MAXIMUM) {
*cause = xstrdup("change is too big or too small");
return (-1);
}
lc->sy = size;
lc->g.sy = size;
if (opposite)
lc->yoff -= change;
lc->g.yoff -= change;
} else {
size = lc->sx + change;
size = lc->g.sx + change;
if (size < PANE_MINIMUM || size > PANE_MAXIMUM) {
*cause = xstrdup("change is too big or too small");
return (-1);
}
lc->sx = size;
lc->g.sx = size;
if (opposite)
lc->xoff -= change;
lc->g.xoff -= change;
}
redraw_invalidate_scene(wp->window);
return (0);
@@ -1082,13 +1086,13 @@ layout_new_pane_size(struct window *w, u_int previous, struct layout_cell *lc,
*/
min = (PANE_MINIMUM + 1) * (count_left - 1);
if (type == LAYOUT_LEFTRIGHT) {
if (lc->sx - available > min)
min = lc->sx - available;
new_size = (lc->sx * size) / previous;
if (lc->g.sx - available > min)
min = lc->g.sx - available;
new_size = (lc->g.sx * size) / previous;
} else {
if (lc->sy - available > min)
min = lc->sy - available;
new_size = (lc->sy * size) / previous;
if (lc->g.sy - available > min)
min = lc->g.sy - available;
new_size = (lc->g.sy * size) / previous;
}
/* Check against the maximum and minimum size. */
@@ -1124,9 +1128,9 @@ layout_set_size_check(struct window *w, struct layout_cell *lc,
return (0);
if (type == LAYOUT_LEFTRIGHT)
previous = lc->sx;
previous = lc->g.sx;
else
previous = lc->sy;
previous = lc->g.sy;
idx = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
@@ -1162,31 +1166,31 @@ static void
layout_resize_child_cells(struct window *w, struct layout_cell *lc)
{
struct layout_cell *lcchild;
u_int previous, available, count, idx;
u_int prev, available, count, idx;
if (lc->type == LAYOUT_WINDOWPANE)
return;
/* What is the current size used? */
count = 0;
previous = 0;
prev = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (!layout_has_tiled(lcchild))
continue;
count++;
if (lc->type == LAYOUT_LEFTRIGHT)
previous += lcchild->sx;
prev += lcchild->g.sx;
else if (lc->type == LAYOUT_TOPBOTTOM)
previous += lcchild->sy;
prev += lcchild->g.sy;
}
previous += (count - 1);
prev += (count - 1);
/* And how much is available? */
available = 0;
if (lc->type == LAYOUT_LEFTRIGHT)
available = lc->sx;
available = lc->g.sx;
else if (lc->type == LAYOUT_TOPBOTTOM)
available = lc->sy;
available = lc->g.sy;
/* Resize children into the new size. */
idx = 0;
@@ -1194,20 +1198,20 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc)
if (!layout_has_tiled(lcchild))
continue;
if (lc->type == LAYOUT_TOPBOTTOM) {
lcchild->sx = lc->sx;
lcchild->xoff = lc->xoff;
lcchild->g.sx = lc->g.sx;
lcchild->g.xoff = lc->g.xoff;
} else {
lcchild->sx = layout_new_pane_size(w, previous, lcchild,
lc->type, lc->sx, count - idx, available);
available -= (lcchild->sx + 1);
lcchild->g.sx = layout_new_pane_size(w, prev, lcchild,
lc->type, lc->g.sx, count - idx, available);
available -= (lcchild->g.sx + 1);
}
if (lc->type == LAYOUT_LEFTRIGHT) {
lcchild->sy = lc->sy;
lcchild->yoff = lc->yoff;
lcchild->g.sy = lc->g.sy;
lcchild->g.yoff = lc->g.yoff;
} else {
lcchild->sy = layout_new_pane_size(w, previous, lcchild,
lc->type, lc->sy, count - idx, available);
available -= (lcchild->sy + 1);
lcchild->g.sy = layout_new_pane_size(w, prev, lcchild,
lc->type, lc->g.sy, count - idx, available);
available -= (lcchild->g.sy + 1);
}
layout_resize_child_cells(w, lcchild);
idx++;
@@ -1227,7 +1231,7 @@ layout_replace_with_node(struct window *w, struct layout_cell *lc,
lcparent = layout_create_cell(lc->parent);
layout_make_node(lcparent, type);
layout_set_size(lcparent, lc->sx, lc->sy, lc->xoff, lc->yoff);
layout_set_size(lcparent, lc->g.sx, lc->g.sy, lc->g.xoff, lc->g.yoff);
if (lc->parent == NULL)
w->layout_root = lcparent;
else
@@ -1246,7 +1250,7 @@ layout_split_check_space(struct window_pane *wp, struct layout_cell *lc,
enum layout_type type)
{
struct style *sb_style = &wp->scrollbar_style;
u_int minimum, sx = lc->sx, sy = lc->sy;
u_int minimum, sx = lc->g.sx, sy = lc->g.sy;
int status;
if (lc->flags & LAYOUT_CELL_FLOATING)
@@ -1285,7 +1289,7 @@ layout_split_sizes(struct layout_cell *lc, int size, int before,
enum layout_type type, u_int *size1, u_int *size2, u_int *saved_size)
{
u_int s1, s2, ss;
u_int sx = lc->sx, sy = lc->sy;
u_int sx = lc->g.sx, sy = lc->g.sy;
if (type == LAYOUT_LEFTRIGHT)
ss = sx;
@@ -1332,10 +1336,10 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
lc = wp->layout_cell;
/* Copy the old cell size. */
sx = lc->sx;
sy = lc->sy;
xoff = lc->xoff;
yoff = lc->yoff;
sx = lc->g.sx;
sy = lc->g.sy;
xoff = lc->g.xoff;
yoff = lc->g.yoff;
/* Check there is enough space for the two new panes. */
if (!layout_split_check_space(wp, lc, type))
@@ -1376,13 +1380,13 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
* must be resized before inserting the new cell.
*/
if (lc->type == LAYOUT_LEFTRIGHT) {
lc->sx = new_size;
lc->g.sx = new_size;
layout_resize_child_cells(wp->window, lc);
lc->sx = saved_size;
lc->g.sx = saved_size;
} else if (lc->type == LAYOUT_TOPBOTTOM) {
lc->sy = new_size;
lc->g.sy = new_size;
layout_resize_child_cells(wp->window, lc);
lc->sy = saved_size;
lc->g.sy = saved_size;
}
resize_first = 1;
@@ -1426,10 +1430,10 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
*/
if (!resize_first && type == LAYOUT_LEFTRIGHT) {
layout_set_size(lc1, size1, sy, xoff, yoff);
layout_set_size(lc2, size2, sy, xoff + lc1->sx + 1, yoff);
layout_set_size(lc2, size2, sy, xoff + lc1->g.sx + 1, yoff);
} else if (!resize_first && type == LAYOUT_TOPBOTTOM) {
layout_set_size(lc1, sx, size1, xoff, yoff);
layout_set_size(lc2, sx, size2, xoff, yoff + lc1->sy + 1);
layout_set_size(lc2, sx, size2, xoff, yoff + lc1->g.sy + 1);
}
if (full_size) {
if (!resize_first)
@@ -1446,8 +1450,8 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
* layout_assign_pane before much else happens!
*/
struct layout_cell *
layout_floating_pane(struct window *w, struct window_pane *wp, u_int sx,
u_int sy, int ox, int oy)
layout_floating_pane(struct window *w, struct window_pane *wp,
struct layout_geometry *lg)
{
struct layout_cell *lc, *lcnew, *lcparent;
@@ -1459,16 +1463,16 @@ layout_floating_pane(struct window *w, struct window_pane *wp, u_int sx,
if (lcparent == NULL) {
/*
* Adding a pane to a root that isn't node. Must create and
* insert a new root.
*/
* Adding a pane to a root that isn't a node. Must create and
* insert a new root.
*/
lcparent = layout_replace_with_node(w, lc, LAYOUT_TOPBOTTOM);
}
lcnew = layout_create_cell(lcparent);
TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry);
lcnew->flags |= LAYOUT_CELL_FLOATING;
layout_set_size(lcnew, sx, sy, ox, oy);
layout_set_size(lcnew, lg->sx, lg->sy, lg->xoff, lg->yoff);
return (lcnew);
}
@@ -1511,12 +1515,12 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
status = window_get_pane_status(w);
if (parent->type == LAYOUT_LEFTRIGHT)
size = parent->sx;
size = parent->g.sx;
else if (parent->type == LAYOUT_TOPBOTTOM) {
if (layout_add_horizontal_border(w, parent, status))
size = parent->sy - 1;
size = parent->g.sy - 1;
else
size = parent->sy;
size = parent->g.sy;
} else
return (0);
if (size < number - 1)
@@ -1537,7 +1541,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
continue;
change = 0;
if (parent->type == LAYOUT_LEFTRIGHT) {
change = each - (int)lc->sx;
change = each - (int)lc->g.sx;
if (remainder > 0) {
change++;
remainder--;
@@ -1552,7 +1556,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
this++;
remainder--;
}
change = this - (int)lc->sy;
change = this - (int)lc->g.sy;
layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change);
}
if (change != 0)
@@ -1649,29 +1653,29 @@ layout_get_floating_cell(struct cmdq_item *item, struct args *args,
char **cause)
{
struct layout_cell *lcnew;
u_int sx = UINT_MAX, sy = UINT_MAX;
int ox = INT_MAX, oy = INT_MAX;
struct layout_geometry fg;
if (layout_floating_args_parse(item, args, lines, w, &sx, &sy, &ox, &oy,
cause) != 0)
layout_geometry_init(&fg);
if (layout_floating_args_parse(item, args, lines, w, &fg, cause) != 0)
return (NULL);
lcnew = layout_floating_pane(w, wp, sx, sy, ox, oy);
window_push_zoom(wp->window, 1, args_has(args, 'Z'));
lcnew = layout_floating_pane(w, wp, &fg);
return (lcnew);
}
int
layout_floating_args_parse(struct cmdq_item *item, struct args *args,
enum pane_lines lines, struct window *w, u_int *sxp, u_int *syp, int *oxp,
int *oyp, char **cause)
enum pane_lines lines, struct window *w, struct layout_geometry *lg,
char **cause)
{
int sx, sy, ox, oy;
char *error = NULL;
sx = *sxp == UINT_MAX ? w->sx / 2 : *sxp;
sy = *syp == UINT_MAX ? w->sy / 4 : *syp;
ox = *oxp == INT_MAX ? INT_MAX : *oxp;
oy = *oyp == INT_MAX ? INT_MAX : *oyp;
sx = lg->sx == UINT_MAX ? w->sx / 2 : lg->sx;
sy = lg->sy == UINT_MAX ? w->sy / 4 : lg->sy;
ox = lg->xoff;
oy = lg->yoff;
if (args_has(args, 'x')) {
sx = args_percentage_and_expand(args, 'x', 0, PANE_MAXIMUM,
@@ -1748,10 +1752,10 @@ layout_floating_args_parse(struct cmdq_item *item, struct args *args,
return (-1);
}
*sxp = sx;
*syp = sy;
*oxp = ox;
*oyp = oy;
lg->sx = sx;
lg->sy = sy;
lg->xoff = ox;
lg->yoff = oy;
return (0);
}
@@ -1780,9 +1784,9 @@ layout_remove_tile(struct window *w, struct layout_cell *lc)
* neighbour.
*/
if (type == LAYOUT_TOPBOTTOM)
change = lc->sy + 1;
change = lc->g.sy + 1;
else
change = lc->sx + 1;
change = lc->g.sx + 1;
layout_resize_adjust(w, lcneighbour, type, change);
}
@@ -1828,9 +1832,9 @@ layout_insert_tile(struct window *w, struct layout_cell *lc)
*/
layout_insert_tile(w, lcparent);
if (type == LAYOUT_LEFTRIGHT)
size1 = lcparent->sx;
size1 = lcparent->g.sx;
else
size1 = lcparent->sy;
size1 = lcparent->g.sy;
layout_resize_set_size(w, lc, type, size1);
} else {
/*
@@ -1848,10 +1852,10 @@ layout_insert_tile(struct window *w, struct layout_cell *lc)
/* Setting opposite of the 'split' size to that of the parent. */
if (lcparent->type == LAYOUT_LEFTRIGHT) {
size1 = lcparent->sy;
size1 = lcparent->g.sy;
type = LAYOUT_TOPBOTTOM;
} else {
size1 = lcparent->sx;
size1 = lcparent->g.sx;
type = LAYOUT_LEFTRIGHT;
}
layout_resize_set_size(w, lc, type, size1);

17
menu.c
View File

@@ -313,6 +313,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
struct mouse_event *m = &event->m;
u_int i;
int count = menu->count, old = md->choice;
int move;
const char *name = NULL;
const struct menu_item *item;
struct cmdq_state *state;
@@ -320,6 +321,14 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
char *error;
if (KEYC_IS_MOUSE(event->key)) {
/*
* A mouse move with no button held reports as a release, so
* treat it as highlight-only: it must never select or close the
* menu, otherwise a menu opened without a button already down
* (such as a submenu opened from another menu) would vanish as
* soon as the mouse moved over it.
*/
move = MOUSE_DRAG(m->b) && MOUSE_RELEASE(m->b);
if (md->flags & MENU_NOMOUSE) {
if (MOUSE_BUTTONS(m->b) != MOUSE_BUTTON_1)
return (1);
@@ -330,7 +339,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
m->y < md->py + 1 ||
m->y > md->py + 1 + count - 1) {
if (~md->flags & MENU_STAYOPEN) {
if (MOUSE_RELEASE(m->b))
if (!move && MOUSE_RELEASE(m->b))
return (1);
} else {
if (!MOUSE_RELEASE(m->b) &&
@@ -345,7 +354,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
return (0);
}
if (~md->flags & MENU_STAYOPEN) {
if (MOUSE_RELEASE(m->b))
if (!move && MOUSE_RELEASE(m->b))
goto chosen;
} else {
if (!MOUSE_WHEEL(m->b) && !MOUSE_DRAG(m->b))
@@ -558,6 +567,10 @@ menu_prepare(struct menu *menu, int flags, int starting_choice,
if (py + menu->count + 2 > c->tty.sy)
py = c->tty.sy - menu->count - 2;
/* Remember where this menu is so -x/-y L can reuse the position. */
c->menu_last_px = px;
c->menu_last_py = py;
if (lines == BOX_LINES_DEFAULT)
lines = options_get_number(o, "menu-border-lines");

View File

@@ -834,6 +834,11 @@ mode_tree_draw(struct mode_tree_data *mtd)
if (mtd->line_size == 0)
return;
w = mtd->width;
h = mtd->height;
if (w == 0 || h == 0)
return;
memcpy(&gc0, &grid_default_cell, sizeof gc0);
memcpy(&gc, &grid_default_cell, sizeof gc);
style_apply(&gc, oo, "tree-mode-selection-style", NULL);
@@ -843,9 +848,6 @@ mode_tree_draw(struct mode_tree_data *mtd)
dfg = gc.fg;
dfg0 = gc0.fg;
w = mtd->width;
h = mtd->height;
screen_write_start(&ctx, s);
screen_write_clearscreen(&ctx, 8);
ft = format_create_defaults(NULL, NULL, NULL, NULL, wp);

View File

@@ -93,6 +93,18 @@ monitor_get_session(struct monitor_set *ms)
return (s);
}
/* Create a format tree for a subscription. */
static struct format_tree *
monitor_create_formats(struct client *c, struct session *s, struct winlink *wl,
struct window_pane *wp)
{
struct format_tree *ft;
ft = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
format_defaults(ft, c, s, wl, wp);
return (ft);
}
/* Compare subscriptions. */
static int
monitor_item_cmp(struct monitor_item *m1, struct monitor_item *m2)
@@ -236,7 +248,7 @@ monitor_check_pane(struct monitor_set *ms, struct monitor_item *me)
if (wl->session != s)
continue;
ft = format_create_defaults(NULL, c, s, wl, wp);
ft = monitor_create_formats(c, s, wl, wp);
value = format_expand(ft, me->format);
format_free(ft);
@@ -314,7 +326,7 @@ monitor_check_window(struct monitor_set *ms, struct monitor_item *me)
if (wl->session != s)
continue;
ft = format_create_defaults(NULL, c, s, wl, NULL);
ft = monitor_create_formats(c, s, wl, NULL);
value = format_expand(ft, me->format);
format_free(ft);
@@ -382,7 +394,7 @@ monitor_check_sessions(struct monitor_set *ms)
struct monitor_item *me, *me1;
struct format_tree *ft;
ft = format_create_defaults(NULL, c, s, NULL, NULL);
ft = monitor_create_formats(c, s, NULL, NULL);
RB_FOREACH_SAFE(me, monitor_items, &ms->items, me1) {
if (me->type == MONITOR_SESSION)
monitor_check_session(ms, me, ft);
@@ -427,7 +439,7 @@ monitor_check_all_panes(struct monitor_set *ms)
ms->generation = 1;
RB_FOREACH(wl, winlinks, &s->windows) {
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
ft = format_create_defaults(NULL, c, s, wl, wp);
ft = monitor_create_formats(c, s, wl, wp);
RB_FOREACH_SAFE(me, monitor_items, &ms->items, me1) {
if (me->type != MONITOR_ALL_PANES)
continue;
@@ -455,7 +467,7 @@ monitor_check_all_windows(struct monitor_set *ms)
if (++ms->generation == 0)
ms->generation = 1;
RB_FOREACH(wl, winlinks, &s->windows) {
ft = format_create_defaults(NULL, c, s, wl, NULL);
ft = monitor_create_formats(c, s, wl, NULL);
RB_FOREACH_SAFE(me, monitor_items, &ms->items, me1) {
if (me->type != MONITOR_ALL_WINDOWS)
continue;

207
options.c
View File

@@ -32,18 +32,69 @@
*/
struct options_array_item {
u_int index;
char *key;
union options_value value;
RB_ENTRY(options_array_item) entry;
};
static int
options_array_key_to_number(const char *key, u_int *idx)
{
const char *errstr;
long long n;
if (*key == '\0')
return (-1);
for (const char *cp = key; *cp != '\0'; cp++) {
if (!isdigit((u_char)*cp))
return (0);
}
n = strtonum(key, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
if (idx != NULL)
*idx = n;
return (1);
}
static char *
options_array_correct_key(const char *key)
{
u_int idx;
int numeric;
char *out;
numeric = options_array_key_to_number(key, &idx);
if (numeric == -1)
return (NULL);
if (numeric == 1) {
xasprintf(&out, "%u", idx);
return (out);
}
return (xstrdup(key));
}
static int
options_array_cmp(struct options_array_item *a1, struct options_array_item *a2)
{
if (a1->index < a2->index)
u_int i1, i2;
int n1, n2;
n1 = options_array_key_to_number(a1->key, &i1);
n2 = options_array_key_to_number(a2->key, &i2);
if (n1 && n2) {
if (i1 < i2)
return (-1);
if (i1 > i2)
return (1);
return (0);
}
if (n1)
return (-1);
if (a1->index > a2->index)
if (n2)
return (1);
return (0);
return (strcmp(a1->key, a2->key));
}
RB_GENERATE_STATIC(options_array, options_array_item, entry, options_array_cmp);
@@ -260,6 +311,7 @@ options_default(struct options *oo, const struct options_table_entry *oe)
{
struct options_entry *o;
union options_value *ov;
char key[32];
u_int i;
struct cmd_parse_result *pr;
@@ -271,8 +323,10 @@ options_default(struct options *oo, const struct options_table_entry *oe)
options_array_assign(o, oe->default_str, NULL);
return (o);
}
for (i = 0; oe->default_arr[i] != NULL; i++)
options_array_set(o, i, oe->default_arr[i], 0, NULL);
for (i = 0; oe->default_arr[i] != NULL; i++) {
xsnprintf(key, sizeof key, "%u", i);
options_array_set(o, key, oe->default_arr[i], 0, NULL);
}
return (o);
}
@@ -393,21 +447,21 @@ options_table_entry(struct options_entry *o)
}
static struct options_array_item *
options_array_item(struct options_entry *o, u_int idx)
options_array_item(struct options_entry *o, const char *key)
{
struct options_array_item a;
a.index = idx;
a.key = (char *)key;
return (RB_FIND(options_array, &o->value.array, &a));
}
static struct options_array_item *
options_array_new(struct options_entry *o, u_int idx)
options_array_new(struct options_entry *o, const char *key)
{
struct options_array_item *a;
a = xcalloc(1, sizeof *a);
a->index = idx;
a->key = xstrdup(key);
RB_INSERT(options_array, &o->value.array, a);
return (a);
}
@@ -417,6 +471,7 @@ options_array_free(struct options_entry *o, struct options_array_item *a)
{
options_value_free(o, &a->value);
RB_REMOVE(options_array, &o->value.array, a);
free(a->key);
free(a);
}
@@ -433,24 +488,45 @@ options_array_clear(struct options_entry *o)
}
union options_value *
options_array_get(struct options_entry *o, u_int idx)
options_array_get(struct options_entry *o, const char *key)
{
struct options_array_item *a;
char *new_key;
if (!OPTIONS_IS_ARRAY(o))
return (NULL);
a = options_array_item(o, idx);
new_key = options_array_correct_key(key);
if (new_key == NULL)
return (NULL);
a = options_array_item(o, new_key);
free(new_key);
if (a == NULL)
return (NULL);
return (&a->value);
}
union options_value *
options_array_getv(struct options_entry *o, const char *fmt, ...)
{
union options_value *ov;
va_list ap;
char *key;
va_start(ap, fmt);
xvasprintf(&key, fmt, ap);
va_end(ap);
ov = options_array_get(o, key);
free(key);
return (ov);
}
int
options_array_set(struct options_entry *o, u_int idx, const char *value,
options_array_set(struct options_entry *o, const char *key, const char *value,
int append, char **cause)
{
struct options_array_item *a;
char *new;
char *new, *new_key;
struct cmd_parse_result *pr;
long long number;
@@ -460,10 +536,18 @@ options_array_set(struct options_entry *o, u_int idx, const char *value,
return (-1);
}
new_key = options_array_correct_key(key);
if (new_key == NULL) {
if (cause != NULL)
xasprintf(cause, "bad array key: %s", key);
return (-1);
}
if (value == NULL) {
a = options_array_item(o, idx);
a = options_array_item(o, new_key);
if (a != NULL)
options_array_free(o, a);
free(new_key);
return (0);
}
@@ -475,50 +559,56 @@ options_array_set(struct options_entry *o, u_int idx, const char *value,
*cause = pr->error;
else
free(pr->error);
free(new_key);
return (-1);
case CMD_PARSE_SUCCESS:
break;
}
a = options_array_item(o, idx);
a = options_array_item(o, new_key);
if (a == NULL)
a = options_array_new(o, idx);
a = options_array_new(o, new_key);
else
options_value_free(o, &a->value);
a->value.cmdlist = pr->cmdlist;
free(new_key);
return (0);
}
if (OPTIONS_IS_STRING(o)) {
a = options_array_item(o, idx);
a = options_array_item(o, new_key);
if (a != NULL && append)
xasprintf(&new, "%s%s", a->value.string, value);
else
new = xstrdup(value);
if (a == NULL)
a = options_array_new(o, idx);
a = options_array_new(o, new_key);
else
options_value_free(o, &a->value);
a->value.string = new;
free(new_key);
return (0);
}
if (o->tableentry->type == OPTIONS_TABLE_COLOUR) {
if ((number = colour_fromstring(value)) == -1) {
xasprintf(cause, "bad colour: %s", value);
free(new_key);
return (-1);
}
a = options_array_item(o, idx);
a = options_array_item(o, new_key);
if (a == NULL)
a = options_array_new(o, idx);
a = options_array_new(o, new_key);
else
options_value_free(o, &a->value);
a->value.number = number;
free(new_key);
return (0);
}
if (cause != NULL)
*cause = xstrdup("wrong array type");
free(new_key);
return (-1);
}
@@ -527,6 +617,7 @@ options_array_assign(struct options_entry *o, const char *s, char **cause)
{
const char *separator;
char *copy, *next, *string;
char key[32];
u_int i;
separator = o->tableentry->separator;
@@ -536,10 +627,11 @@ options_array_assign(struct options_entry *o, const char *s, char **cause)
if (*s == '\0')
return (0);
for (i = 0; i < UINT_MAX; i++) {
if (options_array_item(o, i) == NULL)
if (options_array_getv(o, "%u", i) == NULL)
break;
}
return (options_array_set(o, i, s, 0, cause));
xsnprintf(key, sizeof key, "%u", i);
return (options_array_set(o, key, s, 0, cause));
}
if (*s == '\0')
@@ -549,12 +641,13 @@ options_array_assign(struct options_entry *o, const char *s, char **cause)
if (*next == '\0')
continue;
for (i = 0; i < UINT_MAX; i++) {
if (options_array_item(o, i) == NULL)
if (options_array_getv(o, "%u", i) == NULL)
break;
}
if (i == UINT_MAX)
break;
if (options_array_set(o, i, next, 0, cause) != 0) {
xsnprintf(key, sizeof key, "%u", i);
if (options_array_set(o, key, next, 0, cause) != 0) {
free(copy);
return (-1);
}
@@ -574,13 +667,13 @@ options_array_first(struct options_entry *o)
struct options_array_item *
options_array_next(struct options_array_item *a)
{
return (RB_NEXT(options_array, &o->value.array, a));
return (RB_NEXT(options_array, , a));
}
u_int
options_array_item_index(struct options_array_item *a)
const char *
options_array_item_key(struct options_array_item *a)
{
return (a->index);
return (a->key);
}
union options_value *
@@ -602,15 +695,16 @@ options_is_string(struct options_entry *o)
}
char *
options_to_string(struct options_entry *o, int idx, int numeric)
options_to_string(struct options_entry *o, const char *key, int numeric)
{
struct options_array_item *a;
char *result = NULL;
char *last = NULL;
char *next;
char *new_key;
if (OPTIONS_IS_ARRAY(o)) {
if (idx == -1) {
if (key == NULL) {
RB_FOREACH(a, options_array, &o->value.array) {
next = options_value_to_string(o, &a->value,
numeric);
@@ -627,7 +721,11 @@ options_to_string(struct options_entry *o, int idx, int numeric)
return (xstrdup(""));
return (result);
}
a = options_array_item(o, idx);
new_key = options_array_correct_key(key);
if (new_key == NULL)
return (xstrdup(""));
a = options_array_item(o, new_key);
free(new_key);
if (a == NULL)
return (xstrdup(""));
return (options_value_to_string(o, &a->value, numeric));
@@ -636,37 +734,41 @@ options_to_string(struct options_entry *o, int idx, int numeric)
}
char *
options_parse(const char *name, int *idx)
options_parse(const char *name, char **key)
{
char *copy, *cp, *end;
char *copy, *cp, *end, *raw, *new_key;
if (*name == '\0')
return (NULL);
*key = NULL;
copy = xstrdup(name);
if ((cp = strchr(copy, '[')) == NULL) {
*idx = -1;
return (copy);
}
end = strchr(cp + 1, ']');
if (end == NULL || end[1] != '\0' || !isdigit((u_char)end[-1])) {
if (end == NULL || end[1] != '\0' || end == cp + 1) {
free(copy);
return (NULL);
}
if (sscanf(cp, "[%d]", idx) != 1 || *idx < 0) {
raw = xstrndup(cp + 1, end - (cp + 1));
new_key = options_array_correct_key(raw);
free(raw);
if (new_key == NULL) {
free(copy);
return (NULL);
}
*key = new_key;
*cp = '\0';
return (copy);
}
struct options_entry *
options_parse_get(struct options *oo, const char *s, int *idx, int only)
options_parse_get(struct options *oo, const char *s, char **key, int only)
{
struct options_entry *o;
char *name;
name = options_parse(s, idx);
name = options_parse(s, key);
if (name == NULL)
return (NULL);
if (only)
@@ -674,6 +776,10 @@ options_parse_get(struct options *oo, const char *s, int *idx, int only)
else
o = options_get(oo, name);
free(name);
if (o == NULL) {
free(*key);
*key = NULL;
}
return (o);
}
@@ -690,14 +796,14 @@ options_search(const char *name)
}
char *
options_match(const char *s, int *idx, int *ambiguous)
options_match(const char *s, char **key, int *ambiguous)
{
const struct options_table_entry *oe, *found;
char *parsed;
const char *name;
size_t namelen;
parsed = options_parse(s, idx);
parsed = options_parse(s, key);
if (parsed == NULL)
return (NULL);
if (*parsed == '@') {
@@ -718,6 +824,8 @@ options_match(const char *s, int *idx, int *ambiguous)
if (found != NULL) {
*ambiguous = 1;
free(parsed);
free(*key);
*key = NULL;
return (NULL);
}
found = oe;
@@ -726,19 +834,21 @@ options_match(const char *s, int *idx, int *ambiguous)
free(parsed);
if (found == NULL) {
*ambiguous = 0;
free(*key);
*key = NULL;
return (NULL);
}
return (xstrdup(found->name));
}
struct options_entry *
options_match_get(struct options *oo, const char *s, int *idx, int only,
options_match_get(struct options *oo, const char *s, char **key, int only,
int *ambiguous)
{
char *name;
struct options_entry *o;
name = options_match(s, idx, ambiguous);
name = options_match(s, key, ambiguous);
if (name == NULL)
return (NULL);
*ambiguous = 0;
@@ -747,6 +857,10 @@ options_match_get(struct options *oo, const char *s, int *idx, int only,
else
o = options_get(oo, name);
free(name);
if (o == NULL) {
free(*key);
*key = NULL;
}
return (o);
}
@@ -1340,11 +1454,12 @@ options_push_changes(const char *name)
}
int
options_remove_or_default(struct options_entry *o, int idx, char **cause)
options_remove_or_default(struct options_entry *o, const char *key,
char **cause)
{
struct options *oo = o->owner;
if (idx == -1) {
if (key == NULL) {
if (o->tableentry != NULL &&
(oo == global_options ||
oo == global_s_options ||
@@ -1352,7 +1467,7 @@ options_remove_or_default(struct options_entry *o, int idx, char **cause)
options_default(oo, o->tableentry);
else
options_remove(o);
} else if (options_array_set(o, idx, NULL, 0, cause) != 0)
} else if (options_array_set(o, key, NULL, 0, cause) != 0)
return (-1);
return (0);
}

View File

@@ -1,22 +1,30 @@
TESTS!= echo *.sh
LOGDIR=logs
.PHONY: all $(TESTS)
.NOTPARALLEL: all $(TESTS)
.PHONY: all
.NOTPARALLEL: all
all:
@failed=0; failures=; \
@mkdir -p "$(LOGDIR)"; \
rm -f "$(LOGDIR)"/*.log; \
failed=0; failures=; \
for test in $(TESTS); do \
base=$${test##*/}; \
log="$(LOGDIR)/$${base%.sh}.log"; \
rm -f "$$log"; \
printf '%-40s ' "$$test"; \
start=$$(date +%s); \
ASAN_OPTIONS="abort_on_error=1:detect_leaks=0:$$ASAN_OPTIONS"; \
env -i LC_CTYPE=C.UTF-8 ASAN_OPTIONS="$$ASAN_OPTIONS" \
sh "$$test" >/dev/null 2>&1; \
sh -x "$$test" >"$$log" 2>&1; \
if [ $$? -eq 0 ]; then \
end=$$(date +%s); \
rm -f "$$log"; \
echo "PASS ($$((end - start))s)"; \
else \
end=$$(date +%s); \
echo "FAIL ($$((end - start))s)"; \
echo " log: $$log"; \
failed=1; \
failures="$$failures $$test"; \
fi; \
@@ -28,9 +36,7 @@ all:
for test in $$failures; do \
echo " $$test"; \
done; \
else \
rmdir "$(LOGDIR)" 2>/dev/null || true; \
fi; \
exit $$failed
$(TESTS):
sh $@
sleep 1

291
regress/alerts.sh Normal file
View File

@@ -0,0 +1,291 @@
#!/bin/sh
# monitor-activity, monitor-bell and monitor-silence: both the
# alert-activity, alert-bell and alert-silence hooks and the winlink alert
# flags (window_activity_flag, window_bell_flag, window_silence_flag and
# the #, !, ~ characters in window_flags). The sessions are detached so
# alert flags are set even on the current window; the *-action options
# still decide whether the hooks fire. Panes run cat: activity is
# generated by the tty echo of send-keys and a bell by sending a BEL and
# newline for cat to write back.
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=$(mktemp -d)
TMUX_TMPDIR="$OUT"
export TMUX_TMPDIR
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
fail()
{
echo "$*" >&2
$TMUX kill-server 2>/dev/null || true
rm -rf "$OUT"
exit 1
}
cleanup()
{
$TMUX kill-server 2>/dev/null || true
rm -rf "$OUT"
}
trap cleanup EXIT
wait_for()
{
option=$1
expected=$2
i=0
while [ $i -lt 30 ]; do
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
[ "$value" = "$expected" ] && return 0
i=$((i + 1))
sleep 0.2
done
fail "expected $option to be '$expected' but got '$value'"
}
assert_unchanged()
{
option=$1
expected=$2
i=0
while [ $i -lt 10 ]; do
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
[ "$value" = "$expected" ] || \
fail "expected $option to remain '$expected' but got '$value'"
i=$((i + 1))
sleep 0.2
done
}
wait_for_fmt()
{
target=$1
fmt=$2
expected=$3
i=0
while [ $i -lt 30 ]; do
value=$($TMUX display -pt "$target" "$fmt" 2>/dev/null || true)
[ "$value" = "$expected" ] && return 0
i=$((i + 1))
sleep 0.2
done
fail "expected $fmt for $target to be '$expected' but got '$value'"
}
assert_fmt_unchanged()
{
target=$1
fmt=$2
expected=$3
i=0
while [ $i -lt 10 ]; do
value=$($TMUX display -pt "$target" "$fmt" 2>/dev/null || true)
[ "$value" = "$expected" ] || \
fail "expected $fmt for $target to remain '$expected' but got '$value'"
i=$((i + 1))
sleep 0.2
done
}
flags_have()
{
target=$1
char=$2
flags=$($TMUX display -pt "$target" '#{window_flags}') ||
fail "display window_flags failed"
case "$flags" in
*"$char"*) ;;
*) fail "expected '$char' in window_flags for $target: '$flags'" ;;
esac
}
flags_lack()
{
target=$1
char=$2
flags=$($TMUX display -pt "$target" '#{window_flags}') ||
fail "display window_flags failed"
case "$flags" in
*"$char"*) fail "unexpected '$char' in window_flags for $target: '$flags'" ;;
esac
}
bell()
{
$TMUX send-keys -t "$1" -H 07 0a || fail "send-keys bell failed"
}
activity()
{
$TMUX send-keys -t "$1" -l x || fail "send-keys activity failed"
}
$TMUX new -d -s mon -n w0 cat || fail "new-session mon failed"
$TMUX set -g @log '' || fail "set @log failed"
$TMUX set-hook -g alert-bell \
'set -gF @log "#{@log}|alert-bell:#{hook_session_name}:#{hook_window_name}"' ||
fail "set-hook alert-bell failed"
$TMUX set-hook -g alert-activity \
'set -gF @log "#{@log}|alert-activity:#{hook_session_name}:#{hook_window_name}"' ||
fail "set-hook alert-activity failed"
$TMUX set-hook -g alert-silence \
'set -gF @log "#{@log}|alert-silence:#{hook_session_name}:#{hook_window_name}"' ||
fail "set-hook alert-silence failed"
# A bell in a non-current window (monitor-bell defaults to on) fires
# alert-bell and sets the bell flag, shown as ! in window_flags.
$TMUX neww -d -t mon: -n bellw cat || fail "new-window bellw failed"
assert_fmt_unchanged mon:bellw '#{window_bell_flag}' 0
flags_lack mon:bellw '!'
bell mon:bellw
wait_for @log '|alert-bell:mon:bellw'
wait_for_fmt mon:bellw '#{window_bell_flag}' 1
flags_have mon:bellw '!'
# Bells are not deduplicated: a second bell fires the hook again even
# though the flag is still set.
bell mon:bellw
wait_for @log '|alert-bell:mon:bellw|alert-bell:mon:bellw'
# Selecting the window clears the alert flags.
$TMUX selectw -t mon:bellw || fail "select-window bellw failed"
wait_for_fmt mon:bellw '#{window_bell_flag}' 0
flags_lack mon:bellw '!'
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
# A bell in the current window of a detached session still sets the flag
# and bell-action any (the default) fires the hook for the current window.
$TMUX set -g @log '' || fail "reset @log failed"
bell mon:w0
wait_for @log '|alert-bell:mon:w0'
wait_for_fmt mon:w0 '#{window_bell_flag}' 1
$TMUX selectw -t mon:bellw || fail "select-window bellw failed"
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
wait_for_fmt mon:w0 '#{window_bell_flag}' 0
# bell-action none: the flag is still set but the hook does not fire.
$TMUX set -t mon bell-action none || fail "set bell-action none failed"
$TMUX set -g @log '' || fail "reset @log failed"
bell mon:bellw
wait_for_fmt mon:bellw '#{window_bell_flag}' 1
assert_unchanged @log ''
$TMUX set -ut mon bell-action || fail "unset bell-action failed"
$TMUX selectw -t mon:bellw || fail "select-window bellw failed"
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
# monitor-bell off: neither the flag nor the hook.
$TMUX set -wt mon:bellw monitor-bell off || fail "set monitor-bell failed"
$TMUX set -g @log '' || fail "reset @log failed"
bell mon:bellw
assert_fmt_unchanged mon:bellw '#{window_bell_flag}' 0
assert_unchanged @log ''
# monitor-activity defaults to off: output sets neither flag nor hook.
$TMUX neww -d -t mon: -n actw cat || fail "new-window actw failed"
$TMUX set -g @log '' || fail "reset @log failed"
activity mon:actw
assert_fmt_unchanged mon:actw '#{window_activity_flag}' 0
assert_unchanged @log ''
# With monitor-activity on, output fires alert-activity and sets the
# activity flag, shown as # in window_flags.
$TMUX set -wt mon:actw monitor-activity on ||
fail "set monitor-activity failed"
activity mon:actw
wait_for @log '|alert-activity:mon:actw'
wait_for_fmt mon:actw '#{window_activity_flag}' 1
flags_have mon:actw '#'
# While the flag is set further activity does not fire the hook again.
$TMUX set -g @log '' || fail "reset @log failed"
activity mon:actw
assert_unchanged @log ''
# On a detached session selecting the window does not clear the activity
# flag: session_set_current treats the selection itself as activity and
# the flag is raised again at once because the session is not attached.
# No hook fires as activity-action other does not apply to the current
# window.
$TMUX selectw -t mon:actw || fail "select-window actw failed"
assert_fmt_unchanged mon:actw '#{window_activity_flag}' 1
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
assert_fmt_unchanged mon:actw '#{window_activity_flag}' 1
assert_unchanged @log ''
# With a client attached, selecting the window does clear the activity
# flag and it stays clear for the current window.
mkfifo "$OUT/fifo" || fail "mkfifo failed"
$TMUX -C attach -t mon <"$OUT/fifo" >"$OUT/control.out" 2>&1 &
exec 3>"$OUT/fifo"
wait_for_fmt mon: '#{session_attached}' 1
$TMUX selectw -t mon:actw || fail "select-window actw failed"
wait_for_fmt mon:actw '#{window_activity_flag}' 0
flags_lack mon:actw '#'
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
exec 3>&-
wait_for_fmt mon: '#{session_attached}' 0
# Once the flag is clear the alert is re-armed.
activity mon:actw
wait_for @log '|alert-activity:mon:actw'
# Activity in the current window of a detached session sets the flag but
# activity-action other (the default) does not fire the hook.
$TMUX set -wt mon:w0 monitor-activity on ||
fail "set monitor-activity w0 failed"
$TMUX set -g @log '' || fail "reset @log failed"
activity mon:w0
wait_for_fmt mon:w0 '#{window_activity_flag}' 1
assert_unchanged @log ''
$TMUX set -wut mon:w0 monitor-activity ||
fail "unset monitor-activity w0 failed"
# monitor-silence: a quiet window fires alert-silence after the interval
# and sets the silence flag, shown as ~ in window_flags. The hook fires
# only once while the flag remains set even though the timer keeps
# running.
$TMUX neww -d -t mon: -n silw cat || fail "new-window silw failed"
assert_fmt_unchanged mon:silw '#{window_silence_flag}' 0
$TMUX set -g @log '' || fail "reset @log failed"
$TMUX set -wt mon:silw monitor-silence 1 || fail "set monitor-silence failed"
wait_for @log '|alert-silence:mon:silw'
wait_for_fmt mon:silw '#{window_silence_flag}' 1
flags_have mon:silw '~'
assert_unchanged @log '|alert-silence:mon:silw'
$TMUX set -wt mon:silw monitor-silence 0 ||
fail "reset monitor-silence failed"
$TMUX selectw -t mon:silw || fail "select-window silw failed"
wait_for_fmt mon:silw '#{window_silence_flag}' 0
flags_lack mon:silw '~'
$TMUX selectw -t mon:w0 || fail "select-window w0 failed"
# A window linked into two sessions: the hook fires once per winlink and
# the flag is set in both sessions. Selecting the window in one session
# clears the flags on every winlink.
$TMUX new -d -s mon2 || fail "new-session mon2 failed"
$TMUX neww -d -t mon: -n shw cat || fail "new-window shw failed"
$TMUX link-window -d -s mon:shw -t mon2:5 || fail "link-window failed"
$TMUX set -g @log '' || fail "reset @log failed"
bell mon:shw
wait_for @log '|alert-bell:mon:shw|alert-bell:mon2:shw'
wait_for_fmt mon:shw '#{window_bell_flag}' 1
wait_for_fmt mon2:5 '#{window_bell_flag}' 1
$TMUX selectw -t mon2:5 || fail "select-window mon2:5 failed"
wait_for_fmt mon2:5 '#{window_bell_flag}' 0
wait_for_fmt mon:shw '#{window_bell_flag}' 0
exit 0

View File

@@ -4,9 +4,9 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
$TMUX2 kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -11,9 +11,9 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMUX_OUTER="$TEST_TMUX -Ltest2 -f/dev/null"
TMUX_OUTER="$TEST_TMUX -LtestB$$ -f/dev/null"
$TMUX_OUTER kill-server 2>/dev/null
trap "$TMUX kill-server 2>/dev/null; $TMUX_OUTER kill-server 2>/dev/null" 0 1 15

View File

@@ -23,7 +23,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMP=$(mktemp)
TMP2=$(mktemp)
trap "rm -f $TMP $TMP2" 0 1 15

View File

@@ -7,7 +7,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
fail()

View File

@@ -17,18 +17,23 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
TMP=$(mktemp -d) || exit 1
TMUX_TMPDIR="$TMP"
export TMUX_TMPDIR
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
cleanup()
{
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
rm -rf "$TMP"
}
trap cleanup EXIT
fail()
{
echo "$1"
cleanup
echo "$1" >&2
exit 1
}
@@ -46,16 +51,50 @@ wait_for()
{
i=0
while [ "$i" -lt 50 ]; do
if capture | grep -q "$1"; then
sleep 0.5
CAPTURED=$(capture)
if printf '%s\n' "$CAPTURED" | grep -F -q "$1"; then
return 0
fi
sleep 0.5
sleep 0.2
i=$((i + 1))
done
fail "timed out waiting for '$1'"
}
# wait_gone $marker
#
# Wait (up to ~10s) until the rendered screen no longer contains $marker.
wait_gone()
{
i=0
while [ "$i" -lt 50 ]; do
CAPTURED=$(capture)
if ! printf '%s\n' "$CAPTURED" | grep -F -q "$1"; then
return 0
fi
sleep 0.2
i=$((i + 1))
done
fail "timed out waiting for '$1' to disappear"
}
# wait_count $marker $n
#
# Wait (up to ~10s) until exactly $n rendered lines contain $marker. The
# matching capture is left in CAPTURED.
wait_count()
{
i=0
while [ "$i" -lt 50 ]; do
CAPTURED=$(capture)
c=$(printf '%s\n' "$CAPTURED" | grep -F -c "$1")
[ "$c" -eq "$2" ] && return 0
sleep 0.2
i=$((i + 1))
done
fail "timed out waiting for $2 lines of '$1' (have $c)"
}
# wait_buffers $n
#
# Wait (up to ~10s) until the test server has exactly $n paste buffers.
@@ -86,10 +125,36 @@ wait_clients()
return 1
}
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
# wait_mode $target $state
#
# Wait (up to ~10s) until a pane enters or leaves mode.
wait_mode()
{
t=$1
want=$2
$TMUX new-session -d -s aaa -x 80 -y 24 || exit 1
i=0
while [ "$i" -lt 50 ]; do
got=$($TMUX display-message -p -t "$t" '#{pane_in_mode}' \
2>/dev/null)
[ "$got" = "$want" ] && return 0
sleep 0.2
i=$((i + 1))
done
fail "pane $t mode state is $got, expected $want"
}
exit_mode()
{
marker=$1
shift
$TMUX send-keys -t aaa:0 "$@" || fail "send-keys $* failed"
wait_mode aaa:0 0
wait_gone "$marker"
}
$TMUX new-session -d -s aaa -x 80 -y 24 'cat' || exit 1
$TMUX2 new-session -d -s out -x 80 -y 24 "$TMUX attach -t aaa" || exit 1
wait_clients 1 || fail "no client attached to test server"
@@ -100,57 +165,70 @@ $TMUX set-buffer -b bufz "other buffer" || exit 1
# --- filter by buffer name ---------------------------------------------------
$TMUX choose-buffer -t aaa:0 -F 'B1' -f '#{==:#{buffer_name},bufa}' || exit 1
wait_for 'B1'
out=$(capture)
echo "$out" | grep -q 'bufa: B1' || fail "bufa missing when it matches"
echo "$out" | grep -q 'bufz: B1' && fail "bufz shown but does not match"
[ "$(echo "$out" | grep -c ': B1')" -eq 1 ] || fail "expected 1 buffer"
$TMUX send-keys -t aaa:0 q
wait_count ': B1' 1
out=$CAPTURED
printf '%s\n' "$out" | grep -F -q 'bufa: B1' || \
fail "bufa missing when it matches"
printf '%s\n' "$out" | grep -F -q 'bufz: B1' && \
fail "bufz shown but does not match"
[ "$(printf '%s\n' "$out" | grep -F -c ': B1')" -eq 1 ] || \
fail "expected 1 buffer"
exit_mode ': B1' q
# --- filter by buffer content ------------------------------------------------
$TMUX choose-buffer -t aaa:0 -F 'B2' -f '#{m:*hello*,#{buffer_sample}}' || \
exit 1
wait_for 'B2'
out=$(capture)
echo "$out" | grep -q 'bufa: B2' || fail "bufa missing when content matches"
echo "$out" | grep -q 'bufz: B2' && fail "bufz shown but content not matched"
$TMUX send-keys -t aaa:0 q
wait_count ': B2' 1
out=$CAPTURED
printf '%s\n' "$out" | grep -F -q 'bufa: B2' || \
fail "bufa missing when content matches"
printf '%s\n' "$out" | grep -F -q 'bufz: B2' && \
fail "bufz shown but content not matched"
exit_mode ': B2' q
# --- no filter shows both buffers ---------------------------------------------
$TMUX choose-buffer -t aaa:0 -F 'B3' || exit 1
wait_for 'B3'
out=$(capture)
echo "$out" | grep -q 'bufa: B3' || fail "bufa missing with no filter"
echo "$out" | grep -q 'bufz: B3' || fail "bufz missing with no filter"
[ "$(echo "$out" | grep -c ': B3')" -eq 2 ] || fail "expected 2 buffers"
$TMUX send-keys -t aaa:0 q
wait_count ': B3' 2
out=$CAPTURED
printf '%s\n' "$out" | grep -F -q 'bufa: B3' || \
fail "bufa missing with no filter"
printf '%s\n' "$out" | grep -F -q 'bufz: B3' || \
fail "bufz missing with no filter"
[ "$(printf '%s\n' "$out" | grep -F -c ': B3')" -eq 2 ] || \
fail "expected 2 buffers"
exit_mode ': B3' q
# --- filter matching nothing ---------------------------------------------------
#
# Everything is shown and the filter indicator reports no matches.
$TMUX choose-buffer -t aaa:0 -F 'B4' -f '#{==:#{buffer_name},nosuch}' || \
exit 1
wait_for 'B4'
out=$(capture)
echo "$out" | grep -q 'bufa: B4' || fail "bufa missing with no-match filter"
echo "$out" | grep -q 'bufz: B4' || fail "bufz missing with no-match filter"
echo "$out" | grep -q 'no matches' || fail "no matches indicator missing"
$TMUX send-keys -t aaa:0 q
wait_count ': B4' 2
out=$CAPTURED
printf '%s\n' "$out" | grep -F -q 'bufa: B4' || \
fail "bufa missing with no-match filter"
printf '%s\n' "$out" | grep -F -q 'bufz: B4' || \
fail "bufz missing with no-match filter"
printf '%s\n' "$out" | grep -F -q 'no matches' || \
fail "no matches indicator missing"
exit_mode ': B4' q
# --- sort orders ---------------------------------------------------------------
#
# By name bufa sorts first and -r reverses.
$TMUX choose-buffer -t aaa:0 -F 'B5' -O name || exit 1
wait_for 'B5'
capture | grep ': B5' | head -1 | grep -q 'bufa: B5' || \
wait_count ': B5' 2
printf '%s\n' "$CAPTURED" | grep -F ': B5' | head -1 | \
grep -F -q 'bufa: B5' || \
fail "bufa not first with -O name"
$TMUX send-keys -t aaa:0 q
exit_mode ': B5' q
$TMUX choose-buffer -t aaa:0 -F 'B6' -O name -r || exit 1
wait_for 'B6'
capture | grep ': B6' | head -1 | grep -q 'bufz: B6' || \
wait_count ': B6' 2
printf '%s\n' "$CAPTURED" | grep -F ': B6' | head -1 | \
grep -F -q 'bufz: B6' || \
fail "bufz not first with -O name -r"
$TMUX send-keys -t aaa:0 q
exit_mode ': B6' q
# --- d deletes the selected buffer --------------------------------------------
#
@@ -159,18 +237,19 @@ $TMUX choose-buffer -t aaa:0 -F 'G1' -f '#{==:#{buffer_name},bufz}' || exit 1
wait_for 'bufz: G1'
$TMUX send-keys -t aaa:0 d
wait_buffers 1
$TMUX list-buffers -F '#{buffer_name}' | grep -q 'bufa' || \
$TMUX list-buffers -F '#{buffer_name}' | grep -F -q 'bufa' || \
fail "wrong buffer deleted"
$TMUX send-keys -t aaa:0 q
exit_mode ': G1' q
# --- C-t tags all buffers and D deletes the tagged ------------------------------
$TMUX set-buffer -b bufz "other buffer" || exit 1
$TMUX set-buffer -b bufb "third buffer" || exit 1
$TMUX choose-buffer -t aaa:0 -F 'G2' || exit 1
wait_for ': G2'
wait_count ': G2' 3
$TMUX send-keys -t aaa:0 C-t D
wait_buffers 0
$TMUX send-keys -t aaa:0 q
wait_mode aaa:0 0
wait_gone ': G2'
# --- Enter runs the default command (paste-buffer) ------------------------------
#
@@ -178,22 +257,15 @@ $TMUX send-keys -t aaa:0 q
# into the shell in the pane, where it appears on the screen.
$TMUX set-buffer -b bufa "hello buffer" || exit 1
$TMUX choose-buffer -t aaa:0 -F 'G3' || exit 1
wait_for 'bufa: G3'
$TMUX send-keys -t aaa:0 Enter
wait_count ': G3' 1
exit_mode ': G3' Enter
i=0
while [ "$i" -lt 50 ]; do
[ "$($TMUX display -p -t aaa:0 '#{pane_in_mode}')" = "0" ] && break
sleep 0.5
i=$((i + 1))
done
[ "$i" -lt 50 ] || fail "mode did not exit after Enter"
i=0
while [ "$i" -lt 50 ]; do
$TMUX capture-pane -p -t aaa:0 | grep -q 'hello buffer' && break
sleep 0.5
$TMUX capture-pane -p -t aaa:0 | grep -F -q 'hello buffer' && \
break
sleep 0.2
i=$((i + 1))
done
[ "$i" -lt 50 ] || fail "buffer not pasted into pane"
cleanup
exit 0

View File

@@ -16,18 +16,23 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
TMP=$(mktemp -d) || exit 1
TMUX_TMPDIR="$TMP"
export TMUX_TMPDIR
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
cleanup()
{
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
rm -rf "$TMP"
}
trap cleanup EXIT
fail()
{
echo "$1"
cleanup
echo "$1" >&2
exit 1
}
@@ -45,16 +50,50 @@ wait_for()
{
i=0
while [ "$i" -lt 50 ]; do
if capture | grep -q "$1"; then
sleep 0.5
CAPTURED=$(capture)
if printf '%s\n' "$CAPTURED" | grep -F -q "$1"; then
return 0
fi
sleep 0.5
sleep 0.2
i=$((i + 1))
done
fail "timed out waiting for '$1'"
}
# wait_gone $marker
#
# Wait (up to ~10s) until the rendered screen no longer contains $marker.
wait_gone()
{
i=0
while [ "$i" -lt 50 ]; do
CAPTURED=$(capture)
if ! printf '%s\n' "$CAPTURED" | grep -F -q "$1"; then
return 0
fi
sleep 0.2
i=$((i + 1))
done
fail "timed out waiting for '$1' to disappear"
}
# wait_count $marker $n
#
# Wait (up to ~10s) until exactly $n rendered lines contain $marker. The
# matching capture is left in CAPTURED.
wait_count()
{
i=0
while [ "$i" -lt 50 ]; do
CAPTURED=$(capture)
c=$(printf '%s\n' "$CAPTURED" | grep -F -c "$1")
[ "$c" -eq "$2" ] && return 0
sleep 0.2
i=$((i + 1))
done
fail "timed out waiting for $2 lines of '$1' (have $c)"
}
# wait_clients $n
#
# Wait (up to ~10s) until the test server has exactly $n clients attached.
@@ -70,14 +109,40 @@ wait_clients()
return 1
}
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
# wait_mode $target $state
#
# Wait (up to ~10s) until a pane enters or leaves mode.
wait_mode()
{
t=$1
want=$2
i=0
while [ "$i" -lt 50 ]; do
got=$($TMUX display-message -p -t "$t" '#{pane_in_mode}' \
2>/dev/null)
[ "$got" = "$want" ] && return 0
sleep 0.2
i=$((i + 1))
done
fail "pane $t mode state is $got, expected $want"
}
exit_mode()
{
marker=$1
shift
$TMUX send-keys -t aaa:0 "$@" || fail "send-keys $* failed"
wait_mode aaa:0 0
wait_gone "$marker"
}
# One client attached to each of two sessions; the mode is displayed on the
# client attached to aaa (in window 0 of the outer server) and the filters
# tell the clients apart by their attached session.
$TMUX new-session -d -s aaa -x 80 -y 24 || exit 1
$TMUX new-session -d -s bbb -x 80 -y 24 || exit 1
$TMUX new-session -d -s aaa -x 80 -y 24 'cat' || exit 1
$TMUX new-session -d -s bbb -x 80 -y 24 'cat' || exit 1
$TMUX2 new-session -d -s out -x 80 -y 24 "$TMUX attach -t aaa" || exit 1
$TMUX2 new-window -d -t out: "$TMUX attach -t bbb" || exit 1
@@ -86,43 +151,55 @@ wait_clients 2 || fail "expected two clients attached to test server"
# --- filter keeping only the aaa client -------------------------------------
$TMUX choose-client -t aaa:0 -F 'C1=#{client_session}' \
-f '#{==:#{client_session},aaa}' || exit 1
wait_for 'C1='
out=$(capture)
echo "$out" | grep -q 'C1=aaa' || fail "aaa client missing when it matches"
echo "$out" | grep -q 'C1=bbb' && fail "bbb client shown but does not match"
[ "$(echo "$out" | grep -c 'C1=')" -eq 1 ] || fail "expected 1 client"
$TMUX send-keys -t aaa:0 q
wait_count 'C1=' 1
out=$CAPTURED
printf '%s\n' "$out" | grep -F -q 'C1=aaa' || \
fail "aaa client missing when it matches"
printf '%s\n' "$out" | grep -F -q 'C1=bbb' && \
fail "bbb client shown but does not match"
[ "$(printf '%s\n' "$out" | grep -F -c 'C1=')" -eq 1 ] || \
fail "expected 1 client"
exit_mode 'C1=' q
# --- filter keeping only the bbb client -------------------------------------
$TMUX choose-client -t aaa:0 -F 'C2=#{client_session}' \
-f '#{==:#{client_session},bbb}' || exit 1
wait_for 'C2='
out=$(capture)
echo "$out" | grep -q 'C2=bbb' || fail "bbb client missing when it matches"
echo "$out" | grep -q 'C2=aaa' && fail "aaa client shown but does not match"
[ "$(echo "$out" | grep -c 'C2=')" -eq 1 ] || fail "expected 1 client"
$TMUX send-keys -t aaa:0 q
wait_count 'C2=' 1
out=$CAPTURED
printf '%s\n' "$out" | grep -F -q 'C2=bbb' || \
fail "bbb client missing when it matches"
printf '%s\n' "$out" | grep -F -q 'C2=aaa' && \
fail "aaa client shown but does not match"
[ "$(printf '%s\n' "$out" | grep -F -c 'C2=')" -eq 1 ] || \
fail "expected 1 client"
exit_mode 'C2=' q
# --- no filter shows both clients -------------------------------------------
$TMUX choose-client -t aaa:0 -F 'C3=#{client_session}' || exit 1
wait_for 'C3='
out=$(capture)
echo "$out" | grep -q 'C3=aaa' || fail "aaa client missing with no filter"
echo "$out" | grep -q 'C3=bbb' || fail "bbb client missing with no filter"
[ "$(echo "$out" | grep -c 'C3=')" -eq 2 ] || fail "expected 2 clients"
$TMUX send-keys -t aaa:0 q
wait_count 'C3=' 2
out=$CAPTURED
printf '%s\n' "$out" | grep -F -q 'C3=aaa' || \
fail "aaa client missing with no filter"
printf '%s\n' "$out" | grep -F -q 'C3=bbb' || \
fail "bbb client missing with no filter"
[ "$(printf '%s\n' "$out" | grep -F -c 'C3=')" -eq 2 ] || \
fail "expected 2 clients"
exit_mode 'C3=' q
# --- filter matching nothing ------------------------------------------------
#
# Everything is shown and the filter indicator reports no matches.
$TMUX choose-client -t aaa:0 -F 'C4=#{client_session}' \
-f '#{==:#{client_session},nosuch}' || exit 1
wait_for 'C4='
out=$(capture)
echo "$out" | grep -q 'C4=aaa' || fail "aaa client missing with no-match filter"
echo "$out" | grep -q 'C4=bbb' || fail "bbb client missing with no-match filter"
echo "$out" | grep -q 'no matches' || fail "no matches indicator missing"
$TMUX send-keys -t aaa:0 q
wait_count 'C4=' 2
out=$CAPTURED
printf '%s\n' "$out" | grep -F -q 'C4=aaa' || \
fail "aaa client missing with no-match filter"
printf '%s\n' "$out" | grep -F -q 'C4=bbb' || \
fail "bbb client missing with no-match filter"
printf '%s\n' "$out" | grep -F -q 'no matches' || \
fail "no matches indicator missing"
exit_mode 'C4=' q
# --- Enter runs the default command (detach-client) --------------------------
#
@@ -131,10 +208,11 @@ $TMUX send-keys -t aaa:0 q
$TMUX choose-client -t aaa:0 -F 'G1=#{client_session}' \
-f '#{==:#{client_session},bbb}' || exit 1
wait_for 'G1=bbb'
$TMUX send-keys -t aaa:0 Enter
$TMUX send-keys -t aaa:0 Enter || fail "send-keys Enter failed"
wait_clients 1 || fail "bbb client did not detach"
wait_mode aaa:0 0
wait_gone 'G1='
[ "$($TMUX list-clients -F '#{client_session}')" = "aaa" ] || \
fail "wrong client detached"
cleanup
exit 0

View File

@@ -24,18 +24,23 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
TMP=$(mktemp -d) || exit 1
TMUX_TMPDIR="$TMP"
export TMUX_TMPDIR
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
cleanup()
{
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
rm -rf "$TMP"
}
trap cleanup EXIT
fail()
{
echo "$1"
cleanup
echo "$1" >&2
exit 1
}
@@ -53,28 +58,30 @@ wait_for()
{
i=0
while [ "$i" -lt 50 ]; do
if capture | grep -q "$1"; then
sleep 0.5
CAPTURED=$(capture)
if printf '%s\n' "$CAPTURED" | grep -F -q "$1"; then
return 0
fi
sleep 0.5
sleep 0.2
i=$((i + 1))
done
fail "timed out waiting for '$1'"
}
# wait_count $marker $n
# wait_count $row-marker $n
#
# Wait (up to ~10s) until exactly $n rendered lines contain $marker.
# Wait (up to ~10s) until exactly $n rendered list rows contain $row-marker.
wait_count()
{
i=0
while [ "$i" -lt 50 ]; do
[ "$(capture | grep -c "$1")" -eq "$2" ] && return 0
sleep 0.5
CAPTURED=$(capture)
c=$(printf '%s\n' "$CAPTURED" | grep -F -c "$1")
[ "$c" -eq "$2" ] && return 0
sleep 0.2
i=$((i + 1))
done
fail "timed out waiting for $2 lines of '$1' (have $(capture | grep -c "$1"))"
fail "timed out waiting for $2 lines of '$1' (have $c)"
}
# wait_clients $n
@@ -92,8 +99,30 @@ wait_clients()
return 1
}
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
# wait_mode $target $state
#
# Wait (up to ~10s) until a pane enters or leaves mode.
wait_mode()
{
t=$1
want=$2
i=0
while [ "$i" -lt 50 ]; do
got=$($TMUX display-message -p -t "$t" '#{pane_in_mode}' \
2>/dev/null)
[ "$got" = "$want" ] && return 0
sleep 0.2
i=$((i + 1))
done
fail "pane $t mode state is $got, expected $want"
}
exit_mode()
{
$TMUX send-keys -t aaa:0 "$@" || fail "send-keys $* failed"
wait_mode aaa:0 0
}
# Session zzz is created first, so it sorts first by index, and has a
# two-pane window 0 and a single-pane window 1. Session aaa has one window
@@ -102,10 +131,10 @@ $TMUX2 kill-server 2>/dev/null
#
# 0 zzz 1 window 0 2 pane 0 3 pane 1 4 window 1 5 pane 0
# 6 aaa 7 window 0 8 pane 0
$TMUX new-session -d -s zzz -x 80 -y 24 || exit 1
$TMUX split-window -t zzz:0 || exit 1
$TMUX new-window -t zzz || exit 1
$TMUX new-session -d -s aaa -x 80 -y 24 || exit 1
$TMUX new-session -d -s zzz -x 80 -y 24 'cat' || exit 1
$TMUX split-window -t zzz:0 'cat' || exit 1
$TMUX new-window -t zzz 'cat' || exit 1
$TMUX new-session -d -s aaa -x 80 -y 24 'cat' || exit 1
$TMUX2 new-session -d -s out -x 80 -y 24 "$TMUX attach -t aaa" || exit 1
wait_clients 1 || fail "no client attached to test server"
@@ -116,45 +145,55 @@ wait_clients 1 || fail "no client attached to test server"
# regression - its two-pane window 0. aaa contributes exactly three lines
# (session, window, pane).
$TMUX choose-tree -t aaa:0 -F 'F1' -f '#{==:#{session_name},aaa}' || exit 1
wait_count 'F1' 3
out=$(capture)
echo "$out" | grep -q 'aaa: F1' || fail "aaa missing when filter matches it"
echo "$out" | grep -q 'zzz: F1' && fail "zzz shown but no pane matches"
$TMUX send-keys -t aaa:0 q
wait_count ': F1' 3
out=$CAPTURED
printf '%s\n' "$out" | grep -F -q 'aaa: F1' || \
fail "aaa missing when filter matches it"
printf '%s\n' "$out" | grep -F -q 'zzz: F1' && \
fail "zzz shown but no pane matches"
exit_mode q
# --- filter keeping only zzz ------------------------------------------------
#
# zzz contributes six lines (session, two windows, three panes); aaa must
# disappear.
$TMUX choose-tree -t aaa:0 -F 'F2' -f '#{==:#{session_name},zzz}' || exit 1
wait_count 'F2' 6
out=$(capture)
echo "$out" | grep -q 'zzz: F2' || fail "zzz missing when filter matches it"
echo "$out" | grep -q 'aaa: F2' && fail "aaa shown but no pane matches"
$TMUX send-keys -t aaa:0 q
wait_count ': F2' 6
out=$CAPTURED
printf '%s\n' "$out" | grep -F -q 'zzz: F2' || \
fail "zzz missing when filter matches it"
printf '%s\n' "$out" | grep -F -q 'aaa: F2' && \
fail "aaa shown but no pane matches"
exit_mode q
# --- filter matching a single pane ------------------------------------------
#
# Only pane 1 of zzz:0 matches, so the tree is exactly session zzz, window 0
# and that pane; zzz:1 and all of aaa must disappear.
$TMUX choose-tree -t aaa:0 -F 'F3' -f '#{==:#{pane_index},1}' || exit 1
wait_count 'F3' 3
out=$(capture)
echo "$out" | grep -q 'zzz: F3' || fail "zzz missing when its pane matches"
echo "$out" | grep -q 'aaa: F3' && fail "aaa shown but no pane matches"
echo "$out" | grep -q '1: F3' || fail "matching pane missing"
$TMUX send-keys -t aaa:0 q
wait_count ': F3' 3
out=$CAPTURED
printf '%s\n' "$out" | grep -F -q 'zzz: F3' || \
fail "zzz missing when its pane matches"
printf '%s\n' "$out" | grep -F -q 'aaa: F3' && \
fail "aaa shown but no pane matches"
printf '%s\n' "$out" | grep -F -q '1: F3' || \
fail "matching pane missing"
exit_mode q
# --- filter matching nothing ------------------------------------------------
#
# Everything is shown and the filter indicator reports no matches.
$TMUX choose-tree -t aaa:0 -F 'F4' -f '#{==:#{session_name},nosuch}' || exit 1
wait_for 'F4'
out=$(capture)
echo "$out" | grep -q 'aaa: F4' || fail "aaa missing with no-match filter"
echo "$out" | grep -q 'zzz: F4' || fail "zzz missing with no-match filter"
echo "$out" | grep -q 'no matches' || fail "no matches indicator missing"
$TMUX send-keys -t aaa:0 q
wait_count ': F4' 9
out=$CAPTURED
printf '%s\n' "$out" | grep -F -q 'aaa: F4' || \
fail "aaa missing with no-match filter"
printf '%s\n' "$out" | grep -F -q 'zzz: F4' || \
fail "zzz missing with no-match filter"
printf '%s\n' "$out" | grep -F -q 'no matches' || \
fail "no matches indicator missing"
exit_mode q
# --- -h with the tree pane as the only match --------------------------------
#
@@ -162,86 +201,93 @@ $TMUX send-keys -t aaa:0 q
# match, so session and window aaa stay listed: two lines, no pane line.
$TMUX choose-tree -h -t aaa:0 -F 'F5' -f '#{==:#{session_name},aaa}' || \
exit 1
wait_count 'F5' 2
capture | grep -q 'aaa: F5' || fail "aaa missing with -h"
$TMUX send-keys -t aaa:0 q
wait_count ': F5' 2
printf '%s\n' "$CAPTURED" | grep -F -q 'aaa: F5' || \
fail "aaa missing with -h"
exit_mode q
# --- sort orders ------------------------------------------------------------
#
# By index zzz (created first) sorts first, by name aaa does, and -r reverses.
$TMUX choose-tree -t aaa:0 -F 'F6' -O index || exit 1
wait_for 'F6'
capture | grep 'F6' | head -1 | grep -q 'zzz: F6' || \
wait_count ': F6' 9
printf '%s\n' "$CAPTURED" | grep -F ': F6' | head -1 | \
grep -F -q 'zzz: F6' || \
fail "zzz not first with -O index"
$TMUX send-keys -t aaa:0 q
exit_mode q
$TMUX choose-tree -t aaa:0 -F 'F7' -O name || exit 1
wait_for 'F7'
capture | grep 'F7' | head -1 | grep -q 'aaa: F7' || \
wait_count ': F7' 9
printf '%s\n' "$CAPTURED" | grep -F ': F7' | head -1 | \
grep -F -q 'aaa: F7' || \
fail "aaa not first with -O name"
$TMUX send-keys -t aaa:0 q
exit_mode q
$TMUX choose-tree -t aaa:0 -F 'F8' -O name -r || exit 1
wait_for 'F8'
capture | grep 'F8' | head -1 | grep -q 'zzz: F8' || \
wait_count ': F8' 9
printf '%s\n' "$CAPTURED" | grep -F ': F8' | head -1 | \
grep -F -q 'zzz: F8' || \
fail "zzz not first with -O name -r"
$TMUX send-keys -t aaa:0 q
exit_mode q
# --- collapse and expand with h and l -----------------------------------------
#
# g moves to the top (session zzz); h collapses it, hiding its five children;
# l expands it again.
$TMUX choose-tree -t aaa:0 -F 'G1' -O index || exit 1
wait_count 'G1' 9
$TMUX send-keys -t aaa:0 g h
wait_count 'G1' 4
$TMUX send-keys -t aaa:0 l
wait_count 'G1' 9
$TMUX send-keys -t aaa:0 q
wait_count ': G1' 9
$TMUX send-keys -t aaa:0 g h || fail "send-keys collapse failed"
wait_count ': G1' 4
$TMUX send-keys -t aaa:0 l || fail "send-keys expand failed"
wait_count ': G1' 9
exit_mode q
# --- filter entered at the prompt with f, cleared with c ----------------------
$TMUX choose-tree -t aaa:0 -F 'G2' -O index || exit 1
wait_count 'G2' 9
$TMUX send-keys -t aaa:0 f
$TMUX send-keys -t aaa:0 -l '#{==:#{session_name},aaa}'
$TMUX send-keys -t aaa:0 Enter
wait_count 'G2' 3
out=$(capture)
echo "$out" | grep -q 'aaa: G2' || fail "aaa missing with prompt filter"
echo "$out" | grep -q 'zzz: G2' && fail "zzz shown with prompt filter"
$TMUX send-keys -t aaa:0 c
wait_count 'G2' 9
$TMUX send-keys -t aaa:0 q
wait_count ': G2' 9
$TMUX send-keys -t aaa:0 f || fail "send-keys f failed"
$TMUX send-keys -t aaa:0 -l '#{==:#{session_name},aaa}' || \
fail "send-keys filter failed"
$TMUX send-keys -t aaa:0 Enter || fail "send-keys Enter failed"
wait_count ': G2' 3
out=$CAPTURED
printf '%s\n' "$out" | grep -F -q 'aaa: G2' || \
fail "aaa missing with prompt filter"
printf '%s\n' "$out" | grep -F -q 'zzz: G2' && \
fail "zzz shown with prompt filter"
$TMUX send-keys -t aaa:0 c || fail "send-keys c failed"
wait_count ': G2' 9
exit_mode q
# --- Enter runs the default command (switch-client) ----------------------------
#
# g selects session zzz and Enter switches the client to it.
$TMUX choose-tree -t aaa:0 -F 'G3' -O index || exit 1
wait_count 'G3' 9
$TMUX send-keys -t aaa:0 g Enter
wait_count ': G3' 9
$TMUX send-keys -t aaa:0 g Enter || fail "send-keys Enter failed"
i=0
while [ "$i" -lt 50 ]; do
[ "$($TMUX list-clients -F '#{client_session}')" = "zzz" ] && break
sleep 0.5
sleep 0.2
i=$((i + 1))
done
[ "$i" -lt 50 ] || fail "client did not switch to zzz"
$TMUX switch-client -c "$($TMUX list-clients -F '#{client_name}')" -t aaa || \
exit 1
wait_mode aaa:0 0
# --- x kills the current item after confirmation -------------------------------
#
# g and four times j select window 1 of zzz; x asks for confirmation and y
# kills it, leaving zzz with one window and the tree with seven lines.
$TMUX choose-tree -t aaa:0 -F 'G4' -O index || exit 1
wait_count 'G4' 9
$TMUX send-keys -t aaa:0 g j j j j x
wait_count ': G4' 9
$TMUX send-keys -t aaa:0 g j j j j x || fail "send-keys x failed"
wait_for 'Kill window 1'
$TMUX send-keys -t aaa:0 y
wait_count 'G4' 7
$TMUX send-keys -t aaa:0 y || fail "send-keys y failed"
wait_count ': G4' 7
[ "$($TMUX list-windows -t zzz -F x | grep -c x)" -eq 1 ] || \
fail "window 1 of zzz not killed"
$TMUX send-keys -t aaa:0 q
exit_mode q
cleanup
exit 0

View File

@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
$TMUX new-session -d -sfoo || exit 1

View File

@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
for i in conf/*.conf; do

View File

@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -8,7 +8,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -7,7 +7,7 @@ LANG=C.UTF-8
export TERM LC_ALL LANG
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest$$ -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMPDIR=$(mktemp -d)
IN="$TMPDIR/in"

View File

@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
$TMUX new -d -x40 -y10 \

View File

@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -f/dev/null -Ltest"
TMUX="$TEST_TMUX -f/dev/null -LtestA$$"
$TMUX kill-server 2>/dev/null
$TMUX new -d -x40 -y10 \

View File

@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -9,7 +9,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
sleep 1

View File

@@ -17,9 +17,9 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
# A second server on its own socket hosts the pane that runs the inner client.
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
cleanup()
{

View File

@@ -19,7 +19,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
# check_value $args $expected
@@ -151,7 +151,7 @@ cat > "$CONF" <<EOF
CFGVAR=fromconfig
%hidden CFGHID=hiddencfg
EOF
CTMUX="$TEST_TMUX -Ltest2 -f$CONF"
CTMUX="$TEST_TMUX -LtestB$$ -f$CONF"
$CTMUX kill-server 2>/dev/null
$CTMUX new-session -d -s c -x 80 -y 24 || { rm -f "$CONF"; exit 1; }
out=$($CTMUX show-environment -g CFGVAR 2>&1)

View File

@@ -15,7 +15,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
fail()

View File

@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
# test_format $format $expected_result
test_format()

View File

@@ -17,7 +17,7 @@ LC_ALL=C.UTF-8
export TZ LANG LC_ALL
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
ESC=$(printf '\033')

View File

@@ -18,8 +18,8 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
cleanup()
{

View File

@@ -9,8 +9,8 @@ export PATH TERM LANG LC_ALL
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Lformat-render-contexts-$$ -f/dev/null"
TMUX2="$TEST_TMUX -Lformat-render-contexts-outer-$$ -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
LIMIT=20000
cleanup()
@@ -49,7 +49,13 @@ tmux_run()
name=$1
shift
out=$(run_cmd $TMUX "$@" 2>&1)
out=$(
if command -v timeout >/dev/null 2>&1; then
timeout 10 $TMUX "$@" 2>&1
else
$TMUX "$@" 2>&1
fi
)
rc=$?
bounded "$name" "$out"
[ "$rc" -eq 0 ] || fail "$name failed: $out"
@@ -58,7 +64,13 @@ tmux_run()
capture()
{
out=$(run_cmd $TMUX2 capture-pane -p -t out:0 2>/dev/null)
out=$(
if command -v timeout >/dev/null 2>&1; then
timeout 10 $TMUX2 capture-pane -p -t out:0 2>/dev/null
else
$TMUX2 capture-pane -p -t out:0 2>/dev/null
fi
)
rc=$?
bounded "capture-pane" "$out"
[ "$rc" -eq 0 ] || fail "capture-pane failed"
@@ -67,7 +79,14 @@ capture()
capture_esc()
{
out=$(run_cmd $TMUX2 capture-pane -pe -t out:0 2>/dev/null)
out=$(
if command -v timeout >/dev/null 2>&1; then
timeout 10 $TMUX2 capture-pane -pe -t out:0 \
2>/dev/null
else
$TMUX2 capture-pane -pe -t out:0 2>/dev/null
fi
)
rc=$?
bounded "capture-pane -e" "$out"
[ "$rc" -eq 0 ] || fail "capture-pane -e failed"

View File

@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
# test_format $format $expected_result
test_format()

View File

@@ -15,10 +15,10 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
# A second server on its own socket provides a real terminal (an inner client
# attached inside one of its panes) so client terminal variables are populated.
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
# Every variable name in format_table[]. Kept as a plain word list so it can be
# iterated with normal shell word splitting.

View File

@@ -7,7 +7,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
$TMUX -f/dev/null has -tfoo </dev/null 2>/dev/null && exit 1

124
regress/hooks-after.sh Normal file
View File

@@ -0,0 +1,124 @@
#!/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=$(mktemp -d)
TMUX_TMPDIR="$OUT"
export TMUX_TMPDIR
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
fail()
{
echo "$*" >&2
$TMUX kill-server 2>/dev/null || true
rm -rf "$OUT"
exit 1
}
cleanup()
{
$TMUX kill-server 2>/dev/null || true
rm -rf "$OUT"
}
trap cleanup EXIT
wait_for()
{
option=$1
expected=$2
i=0
while [ $i -lt 30 ]; do
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
[ "$value" = "$expected" ] && return 0
i=$((i + 1))
sleep 0.2
done
fail "expected $option to be '$expected' but got '$value'"
}
assert_unchanged()
{
option=$1
expected=$2
i=0
while [ $i -lt 10 ]; do
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
[ "$value" = "$expected" ] || \
fail "expected $option to remain '$expected' but got '$value'"
i=$((i + 1))
sleep 0.2
done
}
$TMUX new -d -s one || fail "new-session one failed"
$TMUX new -d -s two || fail "new-session two failed"
# An after hook fires with the command arguments as formats.
$TMUX set -g @after 0 || fail "set @after failed"
$TMUX set-hook -g after-rename-window \
'set -gF @after "#{hook}|#{hook_argument_0}|#{hook_flag_t}"' ||
fail "set-hook -g after-rename-window failed"
$TMUX rename-window -t one:0 first || fail "rename-window first failed"
wait_for @after 'after-rename-window|first|one:0'
# An appended hook command runs after the first.
$TMUX set -g @after2 0 || fail "set @after2 failed"
$TMUX set-hook -ga after-rename-window \
'set -gF @after2 "#{@after}+2"' ||
fail "set-hook -ga after-rename-window failed"
$TMUX rename-window -t one:0 second || fail "rename-window second failed"
wait_for @after 'after-rename-window|second|one:0'
wait_for @after2 'after-rename-window|second|one:0+2'
$TMUX set-hook -gu after-rename-window || fail "set-hook -gu failed"
# A session after hook only fires for commands targeting that session and
# the hook commands run with the command target as current state.
$TMUX set -g @safter 0 || fail "set @safter failed"
$TMUX set-hook -t two after-rename-window \
'set -gF @safter "#{hook}:#{session_name}"' ||
fail "set-hook -t two after-rename-window failed"
$TMUX rename-window -t one:0 third || fail "rename-window third failed"
assert_unchanged @safter 0
$TMUX rename-window -t two:0 fourth || fail "rename-window fourth failed"
wait_for @safter 'after-rename-window:two'
$TMUX set-hook -u -t two after-rename-window ||
fail "set-hook -u -t two failed"
# The command-error hook fires when a command fails.
$TMUX set -g @error 0 || fail "set @error failed"
$TMUX set-hook -g command-error 'set -gF @error "#{hook}"' ||
fail "set-hook -g command-error failed"
if $TMUX rename-window -t nosuchsession:0 x 2>/dev/null; then
fail "rename-window to missing session succeeded"
fi
wait_for @error 'command-error'
$TMUX set-hook -gu command-error || fail "set-hook -gu command-error failed"
# Commands run from a hook do not fire their own after hooks.
$TMUX set -g @copy 0 || fail "set @copy failed"
$TMUX set-hook -g after-copy-mode 'set -gF @copy "#{hook}"' ||
fail "set-hook -g after-copy-mode failed"
$TMUX copy-mode -t one:0 || fail "copy-mode failed"
wait_for @copy 'after-copy-mode'
$TMUX send-keys -t one:0 -X cancel || fail "cancel failed"
$TMUX set -g @copy 0 || fail "reset @copy failed"
$TMUX set -g @ran 0 || fail "set @ran failed"
$TMUX set-hook -g after-rename-window 'copy-mode -t one:0' ||
fail "set-hook -g after-rename-window nested failed"
$TMUX set-hook -ga after-rename-window 'set -g @ran 1' ||
fail "set-hook -ga after-rename-window nested failed"
$TMUX rename-window -t one:0 fifth || fail "rename-window fifth failed"
wait_for @ran 1
mode=$($TMUX display -pt one:0 '#{pane_in_mode}') ||
fail "display pane_in_mode failed"
[ "$mode" = 1 ] || fail "hook did not enter copy mode"
assert_unchanged @copy 0
exit 0

162
regress/hooks-lifecycle.sh Normal file
View File

@@ -0,0 +1,162 @@
#!/bin/sh
# End-of-life hooks: pane-exited, pane-died, window-unlinked and
# session-closed when the pane, window or session they refer to is being
# or has been destroyed. Each hook appends to @log so the order hooks fire
# in is checked as well as the hook formats for the dead objects.
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=$(mktemp -d)
TMUX_TMPDIR="$OUT"
export TMUX_TMPDIR
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
fail()
{
echo "$*" >&2
$TMUX kill-server 2>/dev/null || true
rm -rf "$OUT"
exit 1
}
cleanup()
{
$TMUX kill-server 2>/dev/null || true
rm -rf "$OUT"
}
trap cleanup EXIT
wait_for()
{
option=$1
expected=$2
i=0
while [ $i -lt 30 ]; do
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
[ "$value" = "$expected" ] && return 0
i=$((i + 1))
sleep 0.2
done
fail "expected $option to be '$expected' but got '$value'"
}
assert_unchanged()
{
option=$1
expected=$2
i=0
while [ $i -lt 10 ]; do
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
[ "$value" = "$expected" ] || \
fail "expected $option to remain '$expected' but got '$value'"
i=$((i + 1))
sleep 0.2
done
}
$TMUX new -d -s main || fail "new-session main failed"
$TMUX set -g @log '' || fail "set @log failed"
$TMUX set-hook -g pane-exited \
'set -gF @log "#{@log}|pane-exited:#{hook_pane}"' ||
fail "set-hook pane-exited failed"
$TMUX set-hook -g window-unlinked \
'set -gF @log "#{@log}|window-unlinked:#{hook_session_name}:#{hook_window_name}"' ||
fail "set-hook window-unlinked failed"
$TMUX set-hook -g session-closed \
'set -gF @log "#{@log}|session-closed:#{hook_session_name}"' ||
fail "set-hook session-closed failed"
# The only pane of the only window of a session exits: pane-exited, then
# window-unlinked, then session-closed, each seeing the dead object in the
# hook formats.
pane=$($TMUX new -d -s doomed -n dwin -P -F '#{pane_id}' 'true') ||
fail "new-session doomed failed"
wait_for @log \
"|pane-exited:$pane|window-unlinked:doomed:dwin|session-closed:doomed"
assert_unchanged @log \
"|pane-exited:$pane|window-unlinked:doomed:dwin|session-closed:doomed"
# The dead pane, window and session cannot be used as targets but the
# server survives.
if $TMUX select-pane -t "$pane" 2>/dev/null; then
fail "dead pane still a valid target"
fi
if $TMUX list-windows -t doomed >/dev/null 2>&1; then
fail "dead session still a valid target"
fi
$TMUX list-panes -sat main >/dev/null || fail "list-panes failed"
$TMUX has -t main || fail "server died after pane exit chain"
# kill-window on the last window: window-unlinked then session-closed but
# no pane-exited for the panes in the killed window.
$TMUX set -g @log '' || fail "reset @log failed"
$TMUX new -d -s doomed2 -n dwin2 || fail "new-session doomed2 failed"
$TMUX splitw -d -t doomed2:0 || fail "split-window doomed2 failed"
$TMUX kill-window -t doomed2:0 || fail "kill-window failed"
wait_for @log '|window-unlinked:doomed2:dwin2|session-closed:doomed2'
assert_unchanged @log '|window-unlinked:doomed2:dwin2|session-closed:doomed2'
$TMUX has -t main || fail "server died after kill-window chain"
# kill-session: session-closed fires first, then window-unlinked for its
# windows. A window linked into another session survives.
$TMUX new -d -s shareA -n shared || fail "new-session shareA failed"
$TMUX new -d -s shareB -n bwin || fail "new-session shareB failed"
$TMUX link-window -s shareA:shared -t shareB:7 || fail "link-window failed"
$TMUX set -g @log '' || fail "reset @log failed"
$TMUX kill-session -t shareA || fail "kill-session shareA failed"
wait_for @log '|session-closed:shareA|window-unlinked:shareA:shared'
name=$($TMUX display -pt shareB:7 '#{window_name}') ||
fail "shared window did not survive"
[ "$name" = shared ] || fail "expected window shared but got $name"
# Killing the surviving session destroys the shared window for real while
# the session is being destroyed.
$TMUX set -g @log '' || fail "reset @log failed"
$TMUX kill-window -t shareB:0 || fail "kill-window bwin failed"
wait_for @log '|window-unlinked:shareB:bwin'
$TMUX set -g @log '' || fail "reset @log failed"
$TMUX kill-session -t shareB || fail "kill-session shareB failed"
wait_for @log '|session-closed:shareB|window-unlinked:shareB:shared'
$TMUX has -t main || fail "server died after kill-session chain"
# A pane-died hook can kill its own dead pane (the hook runs with the dead
# pane as current target). The kill-pane runs without hooks so pane-exited
# does not fire.
$TMUX set -g @log '' || fail "reset @log failed"
$TMUX new -d -s roe -n rwin || fail "new-session roe failed"
$TMUX set -wt roe:0 remain-on-exit on || fail "set remain-on-exit failed"
$TMUX set-hook -g pane-died \
'set -gF @log "#{@log}|pane-died:#{hook_pane}" ; kill-pane' ||
fail "set-hook pane-died failed"
pane=$($TMUX splitw -d -t roe:0 -P -F '#{pane_id}' 'true') ||
fail "split-window roe failed"
wait_for @log "|pane-died:$pane"
assert_unchanged @log "|pane-died:$pane"
if $TMUX select-pane -t "$pane" 2>/dev/null; then
fail "pane-died hook did not kill its pane"
fi
$TMUX list-panes -t roe:0 >/dev/null || fail "surviving pane broken"
$TMUX has -t roe || fail "session roe died"
$TMUX set-hook -gu pane-died || fail "unset pane-died failed"
$TMUX kill-session -t roe || fail "kill-session roe failed"
# Killing the last session with end-of-life hooks still set: the server
# runs the hooks and exits cleanly.
$TMUX kill-session -t main || fail "kill-session main failed"
i=0
while $TMUX has 2>/dev/null; do
i=$((i + 1))
[ $i -lt 30 ] || fail "server still running after last session killed"
sleep 0.2
done
exit 0

199
regress/hooks-notify.sh Normal file
View File

@@ -0,0 +1,199 @@
#!/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=$(mktemp -d)
TMUX_TMPDIR="$OUT"
export TMUX_TMPDIR
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
fail()
{
echo "$*" >&2
$TMUX kill-server 2>/dev/null || true
rm -rf "$OUT"
exit 1
}
cleanup()
{
$TMUX kill-server 2>/dev/null || true
rm -rf "$OUT"
}
trap cleanup EXIT
wait_for()
{
option=$1
expected=$2
i=0
while [ $i -lt 30 ]; do
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
[ "$value" = "$expected" ] && return 0
i=$((i + 1))
sleep 0.2
done
fail "expected $option to be '$expected' but got '$value'"
}
$TMUX new -d -s main || fail "new-session main failed"
# session-created, session-renamed, session-closed.
$TMUX set -g @s 0 || fail "set @s failed"
$TMUX set-hook -g session-created \
'set -gF @s "#{hook}:#{hook_session_name}"' ||
fail "set-hook session-created failed"
$TMUX new -d -s tmp || fail "new-session tmp failed"
wait_for @s 'session-created:tmp'
$TMUX set-hook -gu session-created || fail "unset session-created failed"
$TMUX set-hook -g session-renamed \
'set -gF @s "#{hook}:#{hook_session_name}"' ||
fail "set-hook session-renamed failed"
$TMUX rename-session -t tmp tmp2 || fail "rename-session failed"
wait_for @s 'session-renamed:tmp2'
$TMUX set-hook -gu session-renamed || fail "unset session-renamed failed"
$TMUX set-hook -g session-closed \
'set -gF @s "#{hook}:#{hook_session_name}"' ||
fail "set-hook session-closed failed"
$TMUX kill-session -t tmp2 || fail "kill-session failed"
wait_for @s 'session-closed:tmp2'
$TMUX set-hook -gu session-closed || fail "unset session-closed failed"
# window-linked and window-unlinked from new-window and kill-window.
$TMUX set -g @w 0 || fail "set @w failed"
$TMUX set-hook -g window-linked \
'set -gF @w "#{hook}:#{hook_session_name}:#{hook_window_name}"' ||
fail "set-hook window-linked failed"
$TMUX set-hook -g window-unlinked \
'set -gF @w "#{hook}:#{hook_session_name}:#{hook_window_name}"' ||
fail "set-hook window-unlinked failed"
$TMUX neww -d -t main: -n mywin || fail "new-window failed"
wait_for @w 'window-linked:main:mywin'
# window-linked and window-unlinked from link-window and unlink-window.
$TMUX new -d -s other || fail "new-session other failed"
$TMUX set -g @w 0 || fail "reset @w failed"
$TMUX link-window -s main:mywin -t other:5 || fail "link-window failed"
wait_for @w 'window-linked:other:mywin'
$TMUX set -g @w 0 || fail "reset @w failed"
$TMUX unlink-window -t other:5 || fail "unlink-window failed"
wait_for @w 'window-unlinked:other:mywin'
$TMUX set -g @w 0 || fail "reset @w failed"
$TMUX kill-window -t main:mywin || fail "kill-window failed"
wait_for @w 'window-unlinked:main:mywin'
$TMUX set-hook -gu window-linked || fail "unset window-linked failed"
$TMUX set-hook -gu window-unlinked || fail "unset window-unlinked failed"
# window-renamed sees the window id.
window=$($TMUX display -pt main:0 '#{window_id}') ||
fail "display window_id failed"
$TMUX set -g @r 0 || fail "set @r failed"
$TMUX set-hook -g window-renamed \
'set -gF @r "#{hook}:#{hook_window}:#{hook_window_name}"' ||
fail "set-hook window-renamed failed"
$TMUX rename-window -t main:0 renamed || fail "rename-window failed"
wait_for @r "window-renamed:$window:renamed"
$TMUX set-hook -gu window-renamed || fail "unset window-renamed failed"
# window-layout-changed from split-window.
$TMUX set -g @l 0 || fail "set @l failed"
$TMUX set-hook -g window-layout-changed \
'set -gF @l "#{hook}:#{hook_window}"' ||
fail "set-hook window-layout-changed failed"
$TMUX splitw -d -t main:0 || fail "split-window failed"
wait_for @l "window-layout-changed:$window"
$TMUX set-hook -gu window-layout-changed ||
fail "unset window-layout-changed failed"
# window-pane-changed from select-pane.
$TMUX set -g @p 0 || fail "set @p failed"
$TMUX set-hook -g window-pane-changed \
'set -gF @p "#{hook}:#{hook_window}"' ||
fail "set-hook window-pane-changed failed"
$TMUX selectp -t main:0.1 || fail "select-pane failed"
wait_for @p "window-pane-changed:$window"
$TMUX set-hook -gu window-pane-changed ||
fail "unset window-pane-changed failed"
# session-window-changed from select-window.
$TMUX neww -d -t main: -n w2 || fail "new-window w2 failed"
$TMUX set -g @c 0 || fail "set @c failed"
$TMUX set-hook -g session-window-changed \
'set -gF @c "#{hook}:#{hook_session_name}"' ||
fail "set-hook session-window-changed failed"
$TMUX selectw -t main:w2 || fail "select-window failed"
wait_for @c 'session-window-changed:main'
$TMUX set-hook -gu session-window-changed ||
fail "unset session-window-changed failed"
# pane-mode-changed from entering and leaving copy mode.
$TMUX set -g @m 0 || fail "set @m failed"
pane=$($TMUX display -pt main:0.0 '#{pane_id}') ||
fail "display pane_id failed"
$TMUX set-hook -g pane-mode-changed \
'set -gF @m "#{hook}:#{hook_pane}:#{pane_in_mode}"' ||
fail "set-hook pane-mode-changed failed"
$TMUX copy-mode -t main:0.0 || fail "copy-mode failed"
wait_for @m "pane-mode-changed:$pane:1"
$TMUX send-keys -t main:0.0 -X cancel || fail "cancel failed"
wait_for @m "pane-mode-changed:$pane:0"
$TMUX set-hook -gu pane-mode-changed || fail "unset pane-mode-changed failed"
# pane-exited when a pane's command exits with remain-on-exit off.
$TMUX set -g @x 0 || fail "set @x failed"
$TMUX set-hook -g pane-exited 'set -gF @x "#{hook}:#{hook_pane}"' ||
fail "set-hook pane-exited failed"
pane=$($TMUX splitw -d -t main:0 -P -F '#{pane_id}' 'true') ||
fail "split-window true failed"
wait_for @x "pane-exited:$pane"
$TMUX set-hook -gu pane-exited || fail "unset pane-exited failed"
# pane-died when a pane's command exits with remain-on-exit on.
$TMUX set -g remain-on-exit on || fail "set remain-on-exit failed"
$TMUX set -g @d 0 || fail "set @d failed"
$TMUX set-hook -g pane-died 'set -gF @d "#{hook}:#{hook_pane}"' ||
fail "set-hook pane-died failed"
pane=$($TMUX splitw -d -t main:0 -P -F '#{pane_id}' 'true') ||
fail "split-window remain-on-exit failed"
wait_for @d "pane-died:$pane"
$TMUX set-hook -gu pane-died || fail "unset pane-died failed"
$TMUX killp -t "$pane" || fail "kill-pane failed"
$TMUX set -g remain-on-exit off || fail "reset remain-on-exit failed"
# pane-title-changed when a pane sets its title.
$TMUX set -g @t 0 || fail "set @t failed"
$TMUX set-hook -g pane-title-changed \
'set -gF @t "#{hook}:#{hook_pane}:#{pane_title}"' ||
fail "set-hook pane-title-changed failed"
pane=$($TMUX splitw -d -t main:0 -P -F '#{pane_id}' \
'printf "\033]2;mytitle\007"; sleep 30') ||
fail "split-window title failed"
wait_for @t "pane-title-changed:$pane:mytitle"
$TMUX set-hook -gu pane-title-changed ||
fail "unset pane-title-changed failed"
$TMUX killp -t "$pane" || fail "kill-pane title failed"
# client-attached and client-detached using a control client.
$TMUX set -g @a 0 || fail "set @a failed"
$TMUX set-hook -g client-attached 'set -gF @a "#{hook}"' ||
fail "set-hook client-attached failed"
$TMUX set-hook -g client-detached 'set -gF @a "#{hook}"' ||
fail "set-hook client-detached failed"
mkfifo "$OUT/fifo" || fail "mkfifo failed"
$TMUX -C attach -t main <"$OUT/fifo" >"$OUT/control.out" 2>&1 &
exec 3>"$OUT/fifo"
wait_for @a 'client-attached'
exec 3>&-
wait_for @a 'client-detached'
$TMUX set-hook -gu client-attached || fail "unset client-attached failed"
$TMUX set-hook -gu client-detached || fail "unset client-detached failed"
exit 0

155
regress/hooks.sh Normal file
View File

@@ -0,0 +1,155 @@
#!/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=$(mktemp -d)
TMUX_TMPDIR="$OUT"
export TMUX_TMPDIR
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
fail()
{
echo "$*" >&2
$TMUX kill-server 2>/dev/null || true
rm -rf "$OUT"
exit 1
}
cleanup()
{
$TMUX kill-server 2>/dev/null || true
rm -rf "$OUT"
}
trap cleanup EXIT
wait_for()
{
option=$1
expected=$2
i=0
while [ $i -lt 30 ]; do
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
[ "$value" = "$expected" ] && return 0
i=$((i + 1))
sleep 0.2
done
fail "expected $option to be '$expected' but got '$value'"
}
assert_unchanged()
{
option=$1
expected=$2
i=0
while [ $i -lt 10 ]; do
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
[ "$value" = "$expected" ] || \
fail "expected $option to remain '$expected' but got '$value'"
i=$((i + 1))
sleep 0.2
done
}
$TMUX new -d -s one || fail "new-session one failed"
# A global hook fires and sees the hook formats.
$TMUX set -g @created 0 || fail "set @created failed"
$TMUX set-hook -g session-created \
'set -gF @created "#{hook}:#{hook_session_name}"' ||
fail "set-hook -g session-created failed"
$TMUX new -d -s two || fail "new-session two failed"
wait_for @created 'session-created:two'
# Hooks are arrays: an appended command runs after the first.
$TMUX set -g @second 0 || fail "set @second failed"
$TMUX set-hook -ga session-created \
'set -gF @second "#{@created}+second"' ||
fail "set-hook -ga session-created failed"
$TMUX new -d -s three || fail "new-session three failed"
wait_for @created 'session-created:three'
wait_for @second 'session-created:three+second'
# show-hooks lists both commands.
shown=$($TMUX show-hooks -g session-created) ||
fail "show-hooks -g failed"
[ "$(echo "$shown" | wc -l)" -eq 2 ] ||
fail "unexpected show-hooks output: $shown"
echo "$shown" | grep -q '^session-created\[0\]' ||
fail "missing first array item: $shown"
echo "$shown" | grep -q '^session-created\[1\]' ||
fail "missing second array item: $shown"
# Unsetting removes the whole hook.
$TMUX set-hook -gu session-created || fail "set-hook -gu failed"
shown=$($TMUX show-hooks -g session-created) ||
fail "show-hooks -g after unset failed"
if echo "$shown" | grep -q '\['; then
fail "show-hooks showed removed hook: $shown"
fi
$TMUX set -g @created 0 || fail "reset @created failed"
$TMUX new -d -s four || fail "new-session four failed"
assert_unchanged @created 0
# An unknown hook name is rejected.
if $TMUX set-hook -g no-such-hook 'display x' 2>/dev/null; then
fail "unknown hook name was accepted"
fi
# A session hook only fires for events in that session.
$TMUX set -g @renamed 0 || fail "set @renamed failed"
$TMUX set-hook -t one window-renamed \
'set -gF @renamed "#{hook}:#{hook_window_name}"' ||
fail "set-hook -t one window-renamed failed"
$TMUX rename-window -t two:0 twoname || fail "rename-window two failed"
assert_unchanged @renamed 0
$TMUX rename-window -t one:0 onename || fail "rename-window one failed"
wait_for @renamed 'window-renamed:onename'
$TMUX set-hook -u -t one window-renamed || fail "set-hook -u failed"
# A pane hook only fires for events in that pane.
pane2=$($TMUX splitw -d -t one:0 -P -F '#{pane_id}') ||
fail "split-window failed"
$TMUX set -g @mode 0 || fail "set @mode failed"
$TMUX set-hook -p -t "$pane2" pane-mode-changed \
'set -gF @mode "#{hook}:#{hook_pane}"' ||
fail "set-hook -p pane-mode-changed failed"
$TMUX copy-mode -t one:0.0 || fail "copy-mode pane 0 failed"
assert_unchanged @mode 0
$TMUX copy-mode -t "$pane2" || fail "copy-mode pane 1 failed"
wait_for @mode "pane-mode-changed:$pane2"
$TMUX send-keys -t "$pane2" -X cancel || fail "cancel failed"
$TMUX set-hook -pu -t "$pane2" pane-mode-changed ||
fail "set-hook -pu failed"
# A window hook only fires for events in that window.
$TMUX set -g @wmode 0 || fail "set @wmode failed"
$TMUX set-hook -w -t two:0 pane-mode-changed \
'set -gF @wmode "#{hook}:#{hook_window_name}"' ||
fail "set-hook -w pane-mode-changed failed"
$TMUX copy-mode -t three:0.0 || fail "copy-mode three failed"
assert_unchanged @wmode 0
$TMUX copy-mode -t two:0.0 || fail "copy-mode two failed"
wait_for @wmode 'pane-mode-changed:twoname'
$TMUX set-hook -wu -t two:0 pane-mode-changed ||
fail "set-hook -wu failed"
# Commands run from a hook do not fire hooks again.
$TMUX set -g @renames '' || fail "set @renames failed"
$TMUX set-hook -g window-renamed \
'set -gF @renames "#{@renames}x" ; rename-window -t three:0 inner' ||
fail "set-hook -g window-renamed failed"
$TMUX rename-window -t three:0 outer || fail "rename-window outer failed"
wait_for @renames 'x'
assert_unchanged @renames 'x'
name=$($TMUX display -pt three:0 '#{window_name}') ||
fail "display window_name failed"
[ "$name" = inner ] || fail "expected window name inner but got $name"
exit 0

View File

@@ -7,7 +7,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -7,7 +7,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -7,7 +7,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -2,7 +2,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMP=$(mktemp)
EXP=$(mktemp)

View File

@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
sleep 1
$TMUX -f/dev/null new -x20 -y2 -d \; set -g escape-time 0 || exit 1

View File

@@ -8,8 +8,8 @@ export PATH TERM LANG LC_ALL
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Linput-reflow-stress-$$ -f/dev/null"
TMUX2="$TEST_TMUX -Linput-reflow-stress-outer-$$ -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
TMP=$(mktemp "${TMPDIR:-/tmp}/input-reflow-stress.XXXXXX") || exit 1
EXP=$(mktemp "${TMPDIR:-/tmp}/input-reflow-stress.XXXXXX") || exit 1

View File

@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
sleep 0.5

View File

@@ -15,7 +15,8 @@ import tempfile
import time
tmux = sys.argv[1]
server = [tmux, "-Ltest", "-f/dev/null"]
label = "testA%d" % os.getpid()
server = [tmux, "-L" + label, "-f/dev/null"]
def run(*args, check=True):
return subprocess.run(server + list(args), check=check,
@@ -25,7 +26,7 @@ def attach():
pid, fd = os.forkpty()
if pid == 0:
os.environ["TERM"] = "xterm-256color"
os.execl(tmux, tmux, "-Ltest", "-f/dev/null", "attach-session",
os.execl(tmux, tmux, "-L" + label, "-f/dev/null", "attach-session",
"-t", "requests")
os.set_blocking(fd, False)
return pid, fd

View File

@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
sleep 1

View File

@@ -8,8 +8,8 @@ export TERM LC_ALL LANG
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Llifecycle-deferred -f/dev/null"
TMUX2="$TEST_TMUX -Llifecycle-deferred-outer -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
TMPDIR=$(mktemp -d)
IN="$TMPDIR/in"

View File

@@ -8,20 +8,30 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMP=$(mktemp -d) || exit 1
TMUX_TMPDIR="$TMP"
export TMUX_TMPDIR
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
cleanup()
cleanup_servers()
{
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
sleep 0.5
}
cleanup()
{
cleanup_servers
rm -rf "$TMP"
}
trap cleanup EXIT
fail()
{
echo "$1"
echo "$1" >&2
cleanup
exit 1
}
@@ -53,7 +63,7 @@ wait_for()
{
i=0
while [ "$i" -lt 50 ]; do
capture | grep -q "$1" && return 0
capture | grep -F -q "$1" && return 0
sleep 0.2
i=$((i + 1))
done
@@ -95,7 +105,7 @@ start_client()
s=$1
cmd=${2:-cat}
cleanup
cleanup_servers
$TMUX new-session -d -s "$s" -n main -x 80 -y 24 "$cmd" || \
fail "$s: new-session failed"
$TMUX2 new-session -d -s out -x 80 -y 24 "$TMUX attach -t $s" || \
@@ -287,7 +297,7 @@ test_copy_mode()
assert_alive "copy-mode exit"
}
cleanup
cleanup_servers
test_choose_tree
test_choose_buffer
test_choose_client

View File

@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -5,7 +5,7 @@
PATH=/bin:/usr/bin
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TERM=$($TMUX start \; show -gv default-terminal)

View File

@@ -8,7 +8,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -3,12 +3,14 @@
# Tests of array options in the options engine (options_array_* in options.c
# and the array handling in cmd-set-option.c / cmd-show-options.c).
#
# Array options are indexed by integer. This exercises: setting a whole array
# from a separator-delimited string; per-index set with option[N]; -a append
# (which lands at the next free index); show ordering by ascending index and
# preservation of gaps; per-index unset with -u; show -v of a single index and
# of a missing index; and per-option separators (user-keys splits only on
# comma, update-environment on space or comma).
# Array options are keyed by string, with numeric-looking keys kept compatible
# with the old numeric forms. This exercises: setting a whole array from a
# separator-delimited string; per-key set with option[key]; -a append (which
# lands at the next free numeric key); show ordering by numeric keys first in
# ascending order followed by string keys in strcmp order; preservation of gaps;
# per-key unset with -u; show -v of a single key and of a missing key; and
# per-option separators (user-keys splits only on comma, update-environment on
# space or comma).
#
# update-environment (session), status-format (session), user-keys (server)
# and command-alias (server) are used as representative array options.
@@ -20,7 +22,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
check_value()
@@ -87,41 +89,59 @@ $TMUX new-session -d -s main -x 80 -y 24 || exit 1
# --- whole-array assignment splits on the separator -----------------------
#
# update-environment has the default " ," separator, so a single string value
# is split into consecutive indices starting at 0.
# is split into consecutive numeric keys starting at 0.
check_ok set -g update-environment "AAA BBB,CCC"
check_array "-g update-environment" "update-environment[0] AAA
update-environment[1] BBB
update-environment[2] CCC"
# --- -a append goes to the next free index --------------------------------
# --- -a append goes to the next free numeric key --------------------------
check_ok set -ga update-environment "DDD"
check_array "-g update-environment" "update-environment[0] AAA
update-environment[1] BBB
update-environment[2] CCC
update-environment[3] DDD"
# --- per-index unset leaves a gap; show preserves order and gaps ----------
# --- per-key unset leaves a gap; show preserves order and gaps ------------
check_ok set -gu update-environment[1]
check_array "-g update-environment" "update-environment[0] AAA
update-environment[2] CCC
update-environment[3] DDD"
# show -v of an existing index returns its value; a missing index is empty.
# show -v of an existing key returns its value; a missing key is empty.
check_value "-gv update-environment[0]" "AAA"
check_value "-gv update-environment[1]" ""
check_ok set -g update-environment[notify] "EEE"
check_ok set -ga update-environment "FFF"
check_array "-g update-environment" "update-environment[0] AAA
update-environment[1] FFF
update-environment[2] CCC
update-environment[3] DDD
update-environment[notify] EEE"
# --- explicit indexed set, including out-of-order and gaps ----------------
# --- explicit keyed set, including out-of-order and gaps ------------------
#
# status-format is a session array; assigning an empty string first clears its
# multi-index default, then set specific indices out of order and confirm show
# sorts by ascending index and keeps the gap at [1].
# multi-index default, then set specific keys out of order and confirm show
# sorts by ascending numeric key and keeps the gap at [1].
check_ok set -g status-format ""
check_array "-g status-format" "status-format"
check_ok set -g status-format[5] "five"
check_ok set -g status-format[0] "zero"
check_ok set -g status-format[2] "two"
check_ok set -g status-format[01] "one"
check_ok set -g status-format[zoom] "zoom"
check_ok set -g status-format[foo-bar] "foo-bar"
check_ok set -g status-format[xterm-256color] "xterm"
check_array "-g status-format" "status-format[0] zero
status-format[1] one
status-format[2] two
status-format[5] five"
status-format[5] five
status-format[foo-bar] foo-bar
status-format[xterm-256color] xterm
status-format[zoom] zoom"
check_value "-gv status-format[01]" "one"
check_ok set -gu status-format[zoom]
check_value "-gv status-format[zoom]" ""
# --- comma-only separator (user-keys) -------------------------------------
#
@@ -133,11 +153,15 @@ user-keys[1] "Two Three"'
# --- command-type array (a hook) ------------------------------------------
#
# Hooks are command arrays: an indexed value is parsed as a command when set
# Hooks are command arrays: a keyed value is parsed as a command when set
# and re-printed from the parsed command list; a syntax error is reported.
check_ok set -g alert-bell[0] "display-message hi"
check_value "-gv alert-bell[0]" "display-message hi"
check_fail "syntax error" set -g alert-bell[0] "if -x {"
check_ok set-hook -g window-renamed[notify] "display-message renamed"
check_value "-gv window-renamed[notify]" "display-message renamed"
check_ok set-hook -gu window-renamed[notify]
check_value "-gv window-renamed[notify]" ""
# --- colour-type array ----------------------------------------------------
#
@@ -146,11 +170,11 @@ check_ok set -w pane-colours[0] red
check_value "-wv pane-colours[0]" "red"
check_fail "bad colour: xxxyyy" set -w pane-colours[1] xxxyyy
# --- -o refuses to overwrite an already-set index -------------------------
# --- -o refuses to overwrite an already-set key ---------------------------
check_ok set -g command-alias[9] "x=list-keys"
check_fail "already set: command-alias[9]" set -go command-alias[9] "y=list-keys"
# --- non-array option rejects index syntax --------------------------------
# --- non-array option rejects key syntax ----------------------------------
#
# status-left is a plain string; indexing it is an error.
check_fail "not an array: status-left[0]" set -g status-left[0] "x"

View File

@@ -17,7 +17,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
# check_value $args $expected

View File

@@ -17,7 +17,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
check_value()

View File

@@ -4,7 +4,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
$TMUX new -d

View File

@@ -30,7 +30,7 @@ LC_ALL=C.UTF-8
export TERM LANG LC_ALL
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
# check_ok $cmd...

View File

@@ -7,8 +7,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="$TEST_TMUX -LtestA$$ -f/dev/null" # outer (host for the client)
IN="$TEST_TMUX -LtestB$$ -f/dev/null" # inner (under test)
$OUT kill-server 2>/dev/null
$IN kill-server 2>/dev/null

View File

@@ -20,8 +20,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="$TEST_TMUX -LtestA$$ -f/dev/null" # outer (host for the client)
IN="$TEST_TMUX -LtestB$$ -f/dev/null" # inner (under test)
$OUT kill-server 2>/dev/null
$IN kill-server 2>/dev/null

View File

@@ -7,7 +7,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)

View File

@@ -17,8 +17,8 @@ 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"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
RESULTS=screen-redraw-results
TMP=$(mktemp)

View File

@@ -18,8 +18,8 @@ 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"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
RESULTS=screen-redraw-results
TMP=$(mktemp)

View File

@@ -16,8 +16,8 @@ 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"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
RESULTS=screen-redraw-results
TMP=$(mktemp)

View File

@@ -18,8 +18,8 @@ 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"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
RESULTS=screen-redraw-results
TMP=$(mktemp)

View File

@@ -18,8 +18,8 @@ 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"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
RESULTS=screen-redraw-results
TMP=$(mktemp)

View File

@@ -17,8 +17,8 @@ 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"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
RESULTS=screen-redraw-results
TMP=$(mktemp)

View File

@@ -16,8 +16,8 @@ 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"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
RESULTS=screen-redraw-results
TMP=$(mktemp)

View File

@@ -20,8 +20,8 @@ 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"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
RESULTS=screen-redraw-results
TMP=$(mktemp)

View File

@@ -24,8 +24,8 @@ 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"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
RESULTS=screen-redraw-results
TMP=$(mktemp)

View File

@@ -13,8 +13,8 @@ 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"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
RESULTS=screen-redraw-results
TMP=$(mktemp)

View File

@@ -10,7 +10,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMP1=$(mktemp)

View File

@@ -27,7 +27,7 @@ LC_ALL=C.UTF-8
export TERM LANG LC_ALL
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
# check_ok $cmd...

View File

@@ -10,7 +10,7 @@ export TERM LC_ALL LANG
OUT=$(mktemp -d)
TMUX_TMPDIR="$OUT"
export TMUX_TMPDIR
TMUX="$TEST_TMUX -Ltest-hook-B-$$ -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
fail()
{

63
regress/set-hook-R.sh Normal file
View File

@@ -0,0 +1,63 @@
#!/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=$(mktemp -d)
TMUX_TMPDIR="$OUT"
export TMUX_TMPDIR
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
fail()
{
echo "$*" >&2
$TMUX kill-server 2>/dev/null || true
rm -rf "$OUT"
exit 1
}
cleanup()
{
$TMUX kill-server 2>/dev/null || true
rm -rf "$OUT"
}
trap cleanup EXIT
wait_for()
{
option=$1
expected=$2
i=0
while [ $i -lt 30 ]; do
value=$($TMUX show -gqv "$option" 2>/dev/null || true)
[ "$value" = "$expected" ] && return 0
i=$((i + 1))
sleep 0.2
done
fail "expected $option to be '$expected' but got '$value'"
}
$TMUX new -d -s one || fail "new-session one failed"
$TMUX new -d -s two || fail "new-session two failed"
pane=$($TMUX display -pt two:0.0 '#{pane_id}') ||
fail "display-message pane failed"
$TMUX set -g @seen 0 || fail "set @seen failed"
$TMUX set-hook -g @manual \
'set -gF @seen "#{hook}:#{session_name}:#{window_index}:#{pane_id}"' ||
fail "set-hook @manual failed"
[ "$($TMUX show -gqv @seen)" = 0 ] ||
fail "hook ran before set-hook -R"
$TMUX set-hook -g -R -t two:0.0 @manual ||
fail "set-hook -R @manual failed"
wait_for @seen "@manual:two:0:$pane"
exit 0

View File

@@ -17,9 +17,9 @@ if command -v bash >/dev/null 2>&1; then
fi
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
$TMUX2 kill-server 2>/dev/null
$TMUX2 -f/dev/null new -d "$TMUX -f/dev/null new -- $shell"

View File

@@ -27,7 +27,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
# check $target $expected [format]

View File

@@ -29,7 +29,7 @@ PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest -f/dev/null"
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
# check $target $expected [format]

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