mirror of
https://github.com/tmux/tmux.git
synced 2026-09-25 06:06:29 +00:00
Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6bf001b3c6 | ||
|
|
aa3f6a49d7 | ||
|
|
b98ce20a10 | ||
|
|
5ea118e3e0 | ||
|
|
b15c62ac07 | ||
|
|
7049aa826a | ||
|
|
19320c6969 | ||
|
|
78c0dc5be0 | ||
|
|
81fa44f263 | ||
|
|
ba1bfeeb9c | ||
|
|
5047e4eb1a | ||
|
|
8a1f4620b0 | ||
|
|
7d32e7f379 | ||
|
|
6671efcaf7 | ||
|
|
858a9d7376 | ||
|
|
980877fb5c | ||
|
|
c7a6a735bc | ||
|
|
381eaaa3a3 |
@@ -27,10 +27,10 @@ jobs:
|
||||
runner: ubuntu-24.04
|
||||
make: make
|
||||
configure: --enable-utf8proc --enable-asan
|
||||
- name: ubuntu-24.04-arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
make: make
|
||||
configure: --enable-utf8proc --enable-asan
|
||||
#- name: ubuntu-24.04-arm64
|
||||
# runner: ubuntu-24.04-arm
|
||||
# make: make
|
||||
# configure: --enable-utf8proc --enable-asan
|
||||
- name: macos-26-arm64
|
||||
runner: macos-26
|
||||
make: gmake
|
||||
@@ -72,14 +72,14 @@ jobs:
|
||||
|
||||
- name: build
|
||||
run: |
|
||||
sh autogen.sh
|
||||
sh autogen.sh
|
||||
./configure ${{ matrix.configure }}
|
||||
${{ matrix.make }} -j"$(getconf _NPROCESSORS_ONLN)"
|
||||
|
||||
- name: test
|
||||
run: |
|
||||
cd regress
|
||||
${{ matrix.make }} -j"$(getconf _NPROCESSORS_ONLN)"
|
||||
${{ matrix.make }}
|
||||
|
||||
- name: logs
|
||||
if: failure()
|
||||
|
||||
+1
-4
@@ -257,8 +257,7 @@ check_PROGRAMS = \
|
||||
fuzz/input-fuzzer \
|
||||
fuzz/cmd-parse-fuzzer \
|
||||
fuzz/format-fuzzer \
|
||||
fuzz/style-fuzzer \
|
||||
fuzz/layout-fuzzer
|
||||
fuzz/style-fuzzer
|
||||
fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
||||
fuzz_input_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
|
||||
fuzz_cmd_parse_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
||||
@@ -267,8 +266,6 @@ fuzz_format_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
||||
fuzz_format_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
|
||||
fuzz_style_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
||||
fuzz_style_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
|
||||
fuzz_layout_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
||||
fuzz_layout_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
|
||||
endif
|
||||
|
||||
# Install tmux.1 in the right format.
|
||||
|
||||
@@ -91,10 +91,8 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
|
||||
wp = target.wp;
|
||||
|
||||
if (wl != NULL) {
|
||||
if (wp != NULL) {
|
||||
window_redraw_active_switch(wp->window, wp);
|
||||
if (wp != NULL)
|
||||
window_set_active_pane(wp->window, wp, 1);
|
||||
}
|
||||
session_set_current(s, wl);
|
||||
if (wp != NULL)
|
||||
cmd_find_from_winlink_pane(current, wl, wp, 0);
|
||||
|
||||
+7
-8
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-display-menu.c,v 1.55 2026/09/21 12:14:32 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-display-menu.c,v 1.54 2026/09/21 10:22:31 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -285,8 +285,8 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
|
||||
enum box_lines lines = BOX_LINES_DEFAULT;
|
||||
char *title, *cause = NULL;
|
||||
int flags = 0, starting_choice = 0;
|
||||
u_int px, py, sx, sy, i, count = args_count(args);
|
||||
struct options *o = target->w->options;
|
||||
u_int px, py, i, count = args_count(args);
|
||||
struct options *o = target->s->curw->window->options;
|
||||
struct options_entry *oe;
|
||||
|
||||
if (args_has(args, 'C')) {
|
||||
@@ -334,6 +334,9 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
if (menu->count == 0)
|
||||
goto out;
|
||||
if (!cmd_display_menu_get_menu_pos(tc, item, args, &px, &py,
|
||||
menu->width + 4, menu->count + 2))
|
||||
goto out;
|
||||
|
||||
value = args_get(args, 'b');
|
||||
if (value != NULL) {
|
||||
@@ -344,11 +347,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
|
||||
cmdq_error(item, "menu-border-lines %s", cause);
|
||||
goto fail;
|
||||
}
|
||||
} else
|
||||
lines = options_get_number(o, "menu-border-lines");
|
||||
menu_get_size(menu, lines, &sx, &sy);
|
||||
if (!cmd_display_menu_get_menu_pos(tc, item, args, &px, &py, sx, sy))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (args_has(args, 'O'))
|
||||
flags |= MENU_STAYOPEN;
|
||||
|
||||
+7
-22
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-join-pane.c,v 1.75 2026/09/21 10:33:16 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-join-pane.c,v 1.74 2026/08/03 20:29:52 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2011 George Nachman <tmux@georgester.com>
|
||||
@@ -133,7 +133,7 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
|
||||
} else if (strcmp(position, "back") == 0) {
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
TAILQ_FOREACH(owp, &w->z_index, zentry) {
|
||||
if (!window_pane_is_floating_with_hidden(owp))
|
||||
if (!window_pane_is_floating(owp))
|
||||
break;
|
||||
}
|
||||
if (owp != NULL)
|
||||
@@ -142,30 +142,24 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
|
||||
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
|
||||
} else if (strcmp(position, "forward") == 0) {
|
||||
owp = TAILQ_PREV(wp, window_panes_zindex, zentry);
|
||||
while (owp != NULL && owp->layout_cell == NULL)
|
||||
owp = TAILQ_PREV(owp, window_panes_zindex, zentry);
|
||||
if (owp != NULL) {
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
TAILQ_INSERT_BEFORE(owp, wp, zentry);
|
||||
}
|
||||
} else if (strcmp(position, "backward") == 0) {
|
||||
owp = TAILQ_NEXT(wp, zentry);
|
||||
while (owp != NULL && owp->layout_cell == NULL)
|
||||
owp = TAILQ_NEXT(owp, zentry);
|
||||
if (owp != NULL && window_pane_is_floating(owp)) {
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry);
|
||||
}
|
||||
} else if (strcmp(position, "forward-loop") == 0) {
|
||||
owp = TAILQ_PREV(wp, window_panes_zindex, zentry);
|
||||
while (owp != NULL && owp->layout_cell == NULL)
|
||||
owp = TAILQ_PREV(owp, window_panes_zindex, zentry);
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
if (owp != NULL)
|
||||
TAILQ_INSERT_BEFORE(owp, wp, zentry);
|
||||
else {
|
||||
TAILQ_FOREACH(owp, &w->z_index, zentry) {
|
||||
if (!window_pane_is_floating_with_hidden(owp))
|
||||
if (!window_pane_is_floating(owp))
|
||||
break;
|
||||
}
|
||||
if (owp != NULL)
|
||||
@@ -175,8 +169,6 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
|
||||
}
|
||||
} else if (strcmp(position, "backward-loop") == 0) {
|
||||
owp = TAILQ_NEXT(wp, zentry);
|
||||
while (owp != NULL && owp->layout_cell == NULL)
|
||||
owp = TAILQ_NEXT(owp, zentry);
|
||||
if (owp != NULL && window_pane_is_floating(owp)) {
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry);
|
||||
@@ -307,7 +299,7 @@ cmd_join_pane_mouse_move(struct client *c, struct mouse_event *m)
|
||||
struct window *w;
|
||||
struct window_pane *wp;
|
||||
struct layout_cell *lc;
|
||||
int y, ly, x, lx, oxoff, oyoff, osx, osy;
|
||||
int y, ly, x, lx;
|
||||
|
||||
wp = cmd_mouse_pane(m, NULL, &wl);
|
||||
if (wp == NULL) {
|
||||
@@ -329,16 +321,10 @@ cmd_join_pane_mouse_move(struct client *c, struct mouse_event *m)
|
||||
ly = m->statusat - 1;
|
||||
|
||||
if (x != lx || y != ly) {
|
||||
oxoff = wp->xoff;
|
||||
oyoff = wp->yoff;
|
||||
osx = wp->sx;
|
||||
osy = wp->sy;
|
||||
|
||||
lc->g.xoff += x - lx;
|
||||
lc->g.yoff += y - ly;
|
||||
layout_fix_panes(w, NULL);
|
||||
|
||||
window_redraw_floating_pane(wp, oxoff, oyoff, osx, osy);
|
||||
server_redraw_window(w);
|
||||
server_redraw_window_borders(w);
|
||||
}
|
||||
}
|
||||
@@ -361,10 +347,8 @@ cmd_join_pane_zindex(struct cmdq_item *item, struct winlink *wl,
|
||||
|
||||
n = 0;
|
||||
TAILQ_FOREACH(owp, &w->z_index, zentry) {
|
||||
if (!window_pane_is_floating_with_hidden(owp))
|
||||
if (!window_pane_is_floating(owp))
|
||||
break;
|
||||
if (owp->layout_cell == NULL)
|
||||
continue;
|
||||
if (n >= z)
|
||||
break;
|
||||
n++;
|
||||
@@ -459,6 +443,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
cmdq_error(item, "pane is not floating");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
server_unzoom_window(dst_w);
|
||||
if ((s = args_get(args, 'P')) != NULL)
|
||||
return (cmd_join_pane_place(item, dst_wl, dst_wp, s));
|
||||
if ((s = args_get(args, 'z')) != NULL)
|
||||
|
||||
+1
-4
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-queue.c,v 1.124 2026/09/22 06:46:50 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-queue.c,v 1.123 2026/08/24 20:34:26 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -580,9 +580,6 @@ cmdq_fire_command(struct cmdq_item *item)
|
||||
int flags, quiet = 0;
|
||||
char *tmp;
|
||||
|
||||
if (item->client != NULL && (item->client->flags & CLIENT_DEAD))
|
||||
return (CMD_RETURN_ERROR);
|
||||
|
||||
if (cfg_finished)
|
||||
cmdq_add_message(item);
|
||||
if (log_get_level() > 1) {
|
||||
|
||||
+5
-8
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-resize-pane.c,v 1.69 2026/09/21 10:33:16 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-resize-pane.c,v 1.68 2026/08/31 07:44:39 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -91,8 +91,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
server_redraw_window(w);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
if (!window_pane_is_floating(wp))
|
||||
server_unzoom_window(w);
|
||||
server_unzoom_window(w);
|
||||
lc = wp->layout_cell; /* may have been replaced by unzoom */
|
||||
|
||||
if (args_has(args, 'x')) {
|
||||
@@ -237,8 +236,8 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
|
||||
struct window_pane *wp;
|
||||
struct layout_cell *lc;
|
||||
int y, ly, x, lx, sx, sy, new_sx, new_sy;
|
||||
int left, right, resizes = 0;
|
||||
int new_xoff, new_yoff, old_xoff, old_yoff;
|
||||
int left, right;
|
||||
int new_xoff, new_yoff, resizes = 0;
|
||||
|
||||
wp = cmd_mouse_pane(m, NULL, &wl);
|
||||
if (wp == NULL) {
|
||||
@@ -249,8 +248,6 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
|
||||
lc = wp->layout_cell;
|
||||
sx = wp->sx;
|
||||
sy = wp->sy;
|
||||
old_xoff = wp->xoff;
|
||||
old_yoff = wp->yoff;
|
||||
left = wp->xoff - 1;
|
||||
right = wp->xoff + sx;
|
||||
if (window_pane_scrollbar_reserve(wp) &&
|
||||
@@ -350,7 +347,7 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c,
|
||||
}
|
||||
if (resizes != 0) {
|
||||
layout_fix_panes(w, NULL);
|
||||
window_redraw_floating_pane(wp, old_xoff, old_yoff, sx, sy);
|
||||
server_redraw_window(w);
|
||||
server_redraw_window_borders(w);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-8
@@ -362,7 +362,6 @@ cmd_split_window_mouse_resize(struct client *c, struct mouse_event *m)
|
||||
enum pane_lines lines;
|
||||
u_int sx, sy;
|
||||
int x, y, xoff, yoff, border;
|
||||
int oxoff, oyoff, osx, osy;
|
||||
|
||||
if (c->tty.mouse_last_pane == -1)
|
||||
return;
|
||||
@@ -418,14 +417,8 @@ cmd_split_window_mouse_resize(struct client *c, struct mouse_event *m)
|
||||
if (sy < PANE_MINIMUM)
|
||||
sy = PANE_MINIMUM;
|
||||
|
||||
oxoff = wp->xoff;
|
||||
oyoff = wp->yoff;
|
||||
osx = wp->sx;
|
||||
osy = wp->sy;
|
||||
|
||||
layout_set_size(lc, sx, sy, xoff, yoff);
|
||||
layout_fix_panes(w, NULL);
|
||||
|
||||
window_redraw_floating_pane(wp, oxoff, oyoff, osx, osy);
|
||||
server_redraw_window(w);
|
||||
server_redraw_window_borders(w);
|
||||
}
|
||||
|
||||
+1
-35
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-wait-for.c,v 1.24 2026/09/22 06:46:50 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-wait-for.c,v 1.23 2026/07/10 13:38:45 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -480,40 +480,6 @@ cmd_wait_for_unlock(struct cmdq_item *item, const char *name,
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
cmd_wait_for_client_lost(struct client *c)
|
||||
{
|
||||
struct wait_channel *wc, *wc1;
|
||||
struct wait_item *wi, *wi1;
|
||||
struct wait_event_item *wei, *wei1;
|
||||
|
||||
TAILQ_FOREACH_SAFE(wei, &wait_event_items, entry, wei1) {
|
||||
if (cmdq_get_client(wei->item) == c) {
|
||||
TAILQ_REMOVE(&wait_event_items, wei, entry);
|
||||
cmdq_continue(wei->item);
|
||||
cmd_wait_for_event_free(wei);
|
||||
}
|
||||
}
|
||||
|
||||
RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
|
||||
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
|
||||
if (cmdq_get_client(wi->item) == c) {
|
||||
cmdq_continue(wi->item);
|
||||
TAILQ_REMOVE(&wc->waiters, wi, entry);
|
||||
free(wi);
|
||||
}
|
||||
}
|
||||
TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) {
|
||||
if (cmdq_get_client(wi->item) == c) {
|
||||
cmdq_continue(wi->item);
|
||||
TAILQ_REMOVE(&wc->lockers, wi, entry);
|
||||
free(wi);
|
||||
}
|
||||
}
|
||||
cmd_wait_for_remove_empty(wc);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
cmd_wait_for_flush(void)
|
||||
{
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Arthur Chan <arthur.chan@adalogics.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Fuzz the custom layout parser.
|
||||
*
|
||||
* A layout string such as "bb62,80x24,0,0{40x24,0,0,1,39x24,41,0,2}" is
|
||||
* accepted by select-layout and is what tmux stores and restores for a
|
||||
* window, so it is parsed from configuration and from commands. It drives
|
||||
* layout-custom.c (the string parser and the checksum), then layout.c, which
|
||||
* resizes and assigns the cells.
|
||||
*
|
||||
* layout_parse() refuses a window with no panes, and refuses a layout whose
|
||||
* cell count is smaller than the pane count, so the window is given a fixed
|
||||
* number of panes. The count is fixed rather than derived from the input, so
|
||||
* a mutation always means a different layout string.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
#define FUZZER_MAXLEN 1024
|
||||
#define FUZZER_PANES 4
|
||||
|
||||
struct event_base *libevent;
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput(const u_char *data, size_t size)
|
||||
{
|
||||
struct window *w;
|
||||
struct window_pane *wp;
|
||||
char *buf, *cause = NULL, *dump;
|
||||
u_int i;
|
||||
|
||||
if (size == 0 || size > FUZZER_MAXLEN)
|
||||
return 0;
|
||||
|
||||
/* layout_parse() takes a C string. */
|
||||
buf = malloc(size + 1);
|
||||
if (buf == NULL)
|
||||
return 0;
|
||||
memcpy(buf, data, size);
|
||||
buf[size] = '\0';
|
||||
|
||||
w = window_create(80, 24, 0, 0);
|
||||
if (w == NULL) {
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
window_add_ref(w, __func__);
|
||||
|
||||
for (i = 0; i < FUZZER_PANES; i++) {
|
||||
wp = window_add_pane(w, NULL, 0, 0);
|
||||
if (w->active == NULL)
|
||||
w->active = wp;
|
||||
}
|
||||
|
||||
if (layout_parse(w, buf, &cause) == 0) {
|
||||
dump = layout_dump(w, w->layout_root, 1);
|
||||
free(dump);
|
||||
}
|
||||
free(cause);
|
||||
|
||||
window_remove_ref(w, __func__);
|
||||
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv)
|
||||
{
|
||||
const struct options_table_entry *oe;
|
||||
|
||||
global_environ = environ_create();
|
||||
global_options = options_create(NULL);
|
||||
global_s_options = options_create(NULL);
|
||||
global_w_options = options_create(NULL);
|
||||
for (oe = options_table; oe->name != NULL; oe++) {
|
||||
if (oe->scope & OPTIONS_TABLE_SERVER)
|
||||
options_default(global_options, oe);
|
||||
if (oe->scope & OPTIONS_TABLE_SESSION)
|
||||
options_default(global_s_options, oe);
|
||||
if (oe->scope & OPTIONS_TABLE_WINDOW)
|
||||
options_default(global_w_options, oe);
|
||||
}
|
||||
|
||||
libevent = osdep_event_init();
|
||||
|
||||
socket_path = xstrdup("dummy");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
# tmux custom layout strings: "<checksum>,<sx>x<sy>,<x>,<y>{...}" / "[...]"
|
||||
","
|
||||
"x"
|
||||
"{"
|
||||
"}"
|
||||
"["
|
||||
"]"
|
||||
"0"
|
||||
"1"
|
||||
"80x24,0,0"
|
||||
"40x24,0,0"
|
||||
"bb62,"
|
||||
"cafe,"
|
||||
",0,0,0"
|
||||
",0,0{"
|
||||
",0,0["
|
||||
"@"
|
||||
@@ -1,2 +0,0 @@
|
||||
[libfuzzer]
|
||||
max_len = 1024
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: key-bindings.c,v 1.193 2026/09/21 20:45:48 nicm Exp $ */
|
||||
/* $OpenBSD: key-bindings.c,v 1.192 2026/08/06 09:05:04 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -523,7 +523,7 @@ key_bindings_init(void)
|
||||
"bind -n TripleClick1Pane { select-pane -t=; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-line; run -d0.3; send -X copy-pipe-and-cancel } }",
|
||||
|
||||
/* Mouse button 1 on border. */
|
||||
"bind -n MouseDown1Border { select-pane -t= }",
|
||||
"bind -n MouseDown1Border { select-pane -M }",
|
||||
|
||||
/* Mouse button 1 drag on border. */
|
||||
"bind -n MouseDrag1Border { resize-pane -M }",
|
||||
|
||||
+5
-37
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: layout-custom.c,v 1.43 2026/09/22 06:48:01 nicm Exp $ */
|
||||
/* $OpenBSD: layout-custom.c,v 1.42 2026/09/20 08:37:47 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -307,37 +307,6 @@ bad:
|
||||
return (xstrdup("0000,"));
|
||||
}
|
||||
|
||||
/* Get a floating pane cell's z-index in the layout being dumped. */
|
||||
static u_int
|
||||
layout_cell_zindex(struct layout_cell *lc)
|
||||
{
|
||||
struct window_pane *wp = lc->wp, *wq;
|
||||
struct window *w = wp->window;
|
||||
struct layout_cell *other;
|
||||
int saved = (lc == wp->saved_layout_cell);
|
||||
u_int i = 0;
|
||||
|
||||
if (saved &&
|
||||
w->active != NULL &&
|
||||
(w->active->flags & PANE_ZOOMED) &&
|
||||
(w->active->saved_layout_cell->flags & LAYOUT_CELL_FLOATING)) {
|
||||
if (wp == w->active)
|
||||
return (0);
|
||||
i++;
|
||||
}
|
||||
TAILQ_FOREACH(wq, &w->z_index, zentry) {
|
||||
if (wq == wp)
|
||||
break;
|
||||
if (saved)
|
||||
other = wq->saved_layout_cell;
|
||||
else
|
||||
other = wq->layout_cell;
|
||||
if (other != NULL && (other->flags & LAYOUT_CELL_FLOATING))
|
||||
i++;
|
||||
}
|
||||
return (i);
|
||||
}
|
||||
|
||||
/* Append information for a single cell in a JSON (v2) format. */
|
||||
static int
|
||||
layout_append_v2(struct layout_cell *lc, struct layout_string *ls)
|
||||
@@ -346,7 +315,7 @@ layout_append_v2(struct layout_cell *lc, struct layout_string *ls)
|
||||
struct window_pane *wp;
|
||||
enum layout_type type;
|
||||
char c;
|
||||
u_int i, n, z;
|
||||
u_int i, n;
|
||||
|
||||
if (lc == NULL)
|
||||
return (-1);
|
||||
@@ -387,10 +356,9 @@ layout_append_v2(struct layout_cell *lc, struct layout_string *ls)
|
||||
if (window_pane_index(wp, &i) != 0)
|
||||
return (-1);
|
||||
layout_string_write(ls, ",\"i\":%u", i);
|
||||
if (lc->flags & LAYOUT_CELL_FLOATING) {
|
||||
z = layout_cell_zindex(lc);
|
||||
layout_string_write(ls, ",\"z\":%u", z);
|
||||
}
|
||||
if ((lc->flags & LAYOUT_CELL_FLOATING) &&
|
||||
window_pane_zindex(wp, &i) == 0)
|
||||
layout_string_write(ls, ",\"z\":%u", i);
|
||||
layout_string_write(ls, ",\"I\":\"%%%u\"", wp->id);
|
||||
}
|
||||
|
||||
|
||||
@@ -469,6 +469,7 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
|
||||
sx = PANE_MINIMUM;
|
||||
else
|
||||
sx = sx - sb_w - sb_pad;
|
||||
wp->flags |= PANE_REDRAWSCROLLBAR;
|
||||
}
|
||||
|
||||
window_pane_resize(wp, sx, sy);
|
||||
@@ -476,11 +477,8 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
|
||||
if (wp->xoff != old_xoff ||
|
||||
wp->yoff != old_yoff ||
|
||||
wp->sx != old_sx ||
|
||||
wp->sy != old_sy) {
|
||||
if (window_pane_scrollbar_reserve(wp))
|
||||
wp->flags |= PANE_REDRAWSCROLLBAR;
|
||||
wp->sy != old_sy)
|
||||
changed = 1;
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
redraw_invalidate_scene(w);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: menu.c,v 1.71 2026/09/21 12:14:32 nicm Exp $ */
|
||||
/* $OpenBSD: menu.c,v 1.70 2026/08/17 07:33:55 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -51,18 +51,6 @@ struct menu_data {
|
||||
void *data;
|
||||
};
|
||||
|
||||
void
|
||||
menu_get_size(struct menu *menu, enum box_lines lines, u_int *sx, u_int *sy)
|
||||
{
|
||||
if (lines == BOX_LINES_NONE) {
|
||||
*sx = menu->item_width + 2;
|
||||
*sy = menu->count;
|
||||
} else {
|
||||
*sx = menu->width + 4;
|
||||
*sy = menu->count + 2;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
menu_add_items(struct menu *menu, const struct menu_item *items,
|
||||
struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs)
|
||||
@@ -154,8 +142,6 @@ menu_add_item(struct menu *menu, const struct menu_item *item,
|
||||
width = format_width(new_item->name);
|
||||
if (*new_item->name == '-')
|
||||
width--;
|
||||
if (width > menu->item_width)
|
||||
menu->item_width = width;
|
||||
if (width > menu->width)
|
||||
menu->width = width;
|
||||
}
|
||||
@@ -252,7 +238,7 @@ menu_update(struct menu_data *md)
|
||||
screen_write_clearscreen(&ctx, 8);
|
||||
|
||||
if (md->border_lines != BOX_LINES_NONE) {
|
||||
screen_write_box(&ctx, menu_width(md), menu_height(md),
|
||||
screen_write_box(&ctx, menu->width + 4, menu->count + 2,
|
||||
md->border_lines, &md->border_style_gc, menu->title);
|
||||
}
|
||||
|
||||
@@ -300,13 +286,11 @@ menu_destroy(struct window *w)
|
||||
void
|
||||
menu_get_cursor(struct menu_data *md, u_int *cx, u_int *cy)
|
||||
{
|
||||
u_int border = (md->border_lines != BOX_LINES_NONE);
|
||||
|
||||
*cx = md->px + 1 + border;
|
||||
*cx = md->px + 2;
|
||||
if (md->choice == -1)
|
||||
*cy = md->py;
|
||||
else
|
||||
*cy = md->py + border + md->choice;
|
||||
*cy = md->py + 1 + md->choice;
|
||||
}
|
||||
|
||||
struct screen *
|
||||
@@ -318,19 +302,13 @@ menu_screen(struct menu_data *md)
|
||||
u_int
|
||||
menu_width(struct menu_data *md)
|
||||
{
|
||||
u_int sx, sy;
|
||||
|
||||
menu_get_size(md->menu, md->border_lines, &sx, &sy);
|
||||
return (sx);
|
||||
return (md->menu->width + 4);
|
||||
}
|
||||
|
||||
u_int
|
||||
menu_height(struct menu_data *md)
|
||||
{
|
||||
u_int sx, sy;
|
||||
|
||||
menu_get_size(md->menu, md->border_lines, &sx, &sy);
|
||||
return (sy);
|
||||
return (md->menu->count + 2);
|
||||
}
|
||||
|
||||
u_int
|
||||
@@ -360,7 +338,6 @@ menu_key(struct client *c, struct menu_data *md, struct key_event *event)
|
||||
enum cmd_parse_status status;
|
||||
char *error;
|
||||
key_code key;
|
||||
u_int border;
|
||||
|
||||
if (KEYC_IS_MOUSE(event->key)) {
|
||||
/*
|
||||
@@ -376,10 +353,10 @@ menu_key(struct client *c, struct menu_data *md, struct key_event *event)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
border = (md->border_lines != BOX_LINES_NONE);
|
||||
if (m->x < md->px || m->x >= md->px + menu_width(md) ||
|
||||
m->y < md->py + border ||
|
||||
m->y >= md->py + border + n) {
|
||||
if (m->x < md->px ||
|
||||
m->x > md->px + 4 + menu->width ||
|
||||
m->y < md->py + 1 ||
|
||||
m->y > md->py + 1 + n - 1) {
|
||||
if (~md->flags & MENU_STAYOPEN) {
|
||||
if (!move && MOUSE_RELEASE(m->b))
|
||||
return (1);
|
||||
@@ -402,7 +379,7 @@ menu_key(struct client *c, struct menu_data *md, struct key_event *event)
|
||||
if (!MOUSE_WHEEL(m->b) && !MOUSE_DRAG(m->b))
|
||||
goto chosen;
|
||||
}
|
||||
md->choice = m->y - (md->py + border);
|
||||
md->choice = m->y - (md->py + 1);
|
||||
if (md->choice != old)
|
||||
server_redraw_window_menu(md->w);
|
||||
return (0);
|
||||
@@ -569,16 +546,20 @@ menu_resize(struct menu_data *md, struct window *w)
|
||||
|
||||
if (md == NULL)
|
||||
return;
|
||||
|
||||
nx = md->px;
|
||||
ny = md->py;
|
||||
|
||||
menu_get_size(md->menu, md->border_lines, &sx, &sy);
|
||||
sx = md->menu->width + 4;
|
||||
sy = md->menu->count + 2;
|
||||
|
||||
if (nx + sx > w->sx) {
|
||||
if (w->sx <= sx)
|
||||
nx = 0;
|
||||
else
|
||||
nx = w->sx - sx;
|
||||
}
|
||||
|
||||
if (ny + sy > w->sy) {
|
||||
if (w->sy <= sy)
|
||||
ny = 0;
|
||||
@@ -610,9 +591,8 @@ menu_display(struct menu *menu, int flags, int starting_choice,
|
||||
w = fs->w;
|
||||
o = w->options;
|
||||
|
||||
if (lines == BOX_LINES_DEFAULT)
|
||||
lines = options_get_number(o, "menu-border-lines");
|
||||
menu_get_size(menu, lines, &sx, &sy);
|
||||
sx = menu->width + 4;
|
||||
sy = menu->count + 2;
|
||||
if (sx >= w->sx)
|
||||
px = 0;
|
||||
else if (px + sx > w->sx)
|
||||
@@ -624,6 +604,9 @@ menu_display(struct menu *menu, int flags, int starting_choice,
|
||||
w->menu_last_px = px;
|
||||
w->menu_last_py = py;
|
||||
|
||||
if (lines == BOX_LINES_DEFAULT)
|
||||
lines = options_get_number(o, "menu-border-lines");
|
||||
|
||||
md = xcalloc(1, sizeof *md);
|
||||
md->w = w;
|
||||
md->flags = flags;
|
||||
|
||||
+6
-11
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: mode-tree.c,v 1.102 2026/09/21 12:14:32 nicm Exp $ */
|
||||
/* $OpenBSD: mode-tree.c,v 1.101 2026/08/05 07:50:21 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -1375,14 +1375,12 @@ static void
|
||||
mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
|
||||
u_int y, int outside)
|
||||
{
|
||||
struct window *w = mtd->wp->window;
|
||||
struct mode_tree_item *mti;
|
||||
struct menu *menu;
|
||||
const struct menu_item *items;
|
||||
struct mode_tree_menu *mtm;
|
||||
char *title;
|
||||
enum box_lines lines;
|
||||
u_int line, sx, sy;
|
||||
u_int line;
|
||||
|
||||
if (mtd->offset + y > mtd->line_size - 1)
|
||||
line = mtd->current;
|
||||
@@ -1407,17 +1405,14 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
|
||||
mtm->line = line;
|
||||
mtd->references++;
|
||||
|
||||
lines = options_get_number(w->options, "menu-border-lines");
|
||||
menu_get_size(menu, lines, &sx, &sy);
|
||||
if (x >= sx / 2)
|
||||
x -= sx / 2;
|
||||
if (x >= (menu->width + 4) / 2)
|
||||
x -= (menu->width + 4) / 2;
|
||||
else
|
||||
x = 0;
|
||||
x += mtd->wp->xoff;
|
||||
y += mtd->wp->yoff;
|
||||
|
||||
if (menu_display(menu, 0, 0, NULL, x, y, c, lines, NULL, NULL, NULL,
|
||||
NULL, mode_tree_menu_callback, mtm) != 0) {
|
||||
if (menu_display(menu, 0, 0, NULL, x, y, c, BOX_LINES_DEFAULT, NULL,
|
||||
NULL, NULL, NULL, mode_tree_menu_callback, mtm) != 0) {
|
||||
mode_tree_remove_ref(mtd);
|
||||
free(mtm);
|
||||
menu_free(menu);
|
||||
|
||||
+20
-23
@@ -1,17 +1,33 @@
|
||||
TESTS!= echo *.sh
|
||||
LOGDIR=logs
|
||||
|
||||
.PHONY: all prepare $(TESTS)
|
||||
.PHONY: all
|
||||
.NOTPARALLEL: all
|
||||
|
||||
all: prepare $(TESTS)
|
||||
@failed=0; failures=; \
|
||||
all:
|
||||
@mkdir -p "$(LOGDIR)"; \
|
||||
rm -f "$(LOGDIR)"/*.log; \
|
||||
failed=0; failures=; \
|
||||
for test in $(TESTS); do \
|
||||
base=$${test##*/}; \
|
||||
log="$(LOGDIR)/$${base%.sh}.log"; \
|
||||
if [ -f "$$log" ]; then \
|
||||
rm -f "$$log"; \
|
||||
printf '%-40s ' "$$test"; \
|
||||
start=$$(date +%s); \
|
||||
env -i LC_CTYPE=C.UTF-8 MallocNanoZone=0 \
|
||||
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; \
|
||||
sleep 1; \
|
||||
done; \
|
||||
if [ "$$failed" -ne 0 ]; then \
|
||||
echo; \
|
||||
@@ -23,22 +39,3 @@ all: prepare $(TESTS)
|
||||
rmdir "$(LOGDIR)" 2>/dev/null || true; \
|
||||
fi; \
|
||||
exit $$failed
|
||||
|
||||
prepare:
|
||||
@mkdir -p "$(LOGDIR)"
|
||||
@rm -f "$(LOGDIR)"/*.log
|
||||
|
||||
$(TESTS): prepare
|
||||
@base="$@"; base=$${base##*/}; \
|
||||
log="$(LOGDIR)/$${base%.sh}.log"; \
|
||||
start=$$(date +%s); \
|
||||
if env -i LC_CTYPE=C.UTF-8 MallocNanoZone=0 \
|
||||
sh -x "$@" >"$$log" 2>&1; then \
|
||||
end=$$(date +%s); \
|
||||
rm -f "$$log"; \
|
||||
printf '%-40s PASS (%ss)\n' "$@" "$$((end - start))"; \
|
||||
else \
|
||||
end=$$(date +%s); \
|
||||
printf '%-40s FAIL (%ss)\n log: %s\n' \
|
||||
"$@" "$$((end - start))" "$$log"; \
|
||||
fi
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Selecting a pane with attach-session from an already attached client must
|
||||
# redraw pane contents when window-style and window-active-style differ.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
INNER="$TEST_TMUX -Lattach-redraw-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lattach-redraw-outer-$$ -f/dev/null"
|
||||
BEFORE=$DIR/before
|
||||
AFTER=$DIR/after
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
wait_for_client()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
CLIENT=$($INNER list-clients -F '#{client_name}' 2>/dev/null)
|
||||
[ -n "$CLIENT" ] && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "inner client did not attach"
|
||||
}
|
||||
|
||||
LEFT=$($INNER new-session -dPF '#{pane_id}' -s inner -x 40 -y 8 \
|
||||
"printf 'LEFT'; exec sleep 100") || exit 1
|
||||
$INNER set-option -g status off || exit 1
|
||||
$INNER set-option -g window-size manual || exit 1
|
||||
$INNER set-option -g default-terminal screen || exit 1
|
||||
$INNER split-window -h -t "$LEFT" "printf 'RIGHT'; exec sleep 100" || exit 1
|
||||
$INNER set-option -w -t "$LEFT" window-style bg=red || exit 1
|
||||
$INNER set-option -w -t "$LEFT" window-active-style bg=blue || exit 1
|
||||
$INNER bind-key -n x attach-session -t "$LEFT" || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 40 -y 8 'sleep 100' || exit 1
|
||||
$OUTER set-option -g status off || exit 1
|
||||
$OUTER set-option -g window-size manual || exit 1
|
||||
$OUTER set-option -g default-terminal screen || exit 1
|
||||
$OUTER respawn-pane -k -t outer:0.0 \
|
||||
"$TEST_TMUX -Lattach-redraw-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||
exit 1
|
||||
|
||||
wait_for_client
|
||||
sleep 1
|
||||
$OUTER send-keys -t outer:0.0 x || exit 1
|
||||
sleep 1
|
||||
[ "$($INNER display-message -p -t inner '#{pane_id}')" = "$LEFT" ] ||
|
||||
fail "attach-session did not select the target pane"
|
||||
$OUTER capture-pane -pe -t outer:0.0 >"$BEFORE" || exit 1
|
||||
|
||||
# A forced redraw produces the correct scene. It must be identical to the
|
||||
# scene drawn immediately by attach-session.
|
||||
$INNER refresh-client -t "$CLIENT" || exit 1
|
||||
sleep 1
|
||||
$OUTER capture-pane -pe -t outer:0.0 >"$AFTER" || exit 1
|
||||
cmp -s "$BEFORE" "$AFTER" ||
|
||||
fail "attach-session left stale active/inactive pane styles"
|
||||
|
||||
exit 0
|
||||
+1
-10
@@ -5,15 +5,6 @@
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
# The pane's shell must not be the user's own interactive shell: a custom
|
||||
# PS1/PROMPT_COMMAND that sets the terminal title (as many do, for tmux/xterm
|
||||
# TERM types) would redraw over the titles this test sets and checks on
|
||||
# every prompt, regardless of how long it waits first.
|
||||
shell=
|
||||
if command -v bash >/dev/null 2>&1; then
|
||||
shell='bash --noprofile --norc +o history'
|
||||
fi
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
@@ -39,7 +30,7 @@ must_equal()
|
||||
[ "$got" = "$want" ] || fail "got '$got', expected '$want'"
|
||||
}
|
||||
|
||||
$TMUX new-session -d -x 80 -y 24 -- $shell || exit 1
|
||||
$TMUX new-session -d -x 80 -y 24 || exit 1
|
||||
$TMUX set-option -qg allow-set-title on || exit 1
|
||||
$TMUX set-option -qg allow-rename on || exit 1
|
||||
$TMUX set-option -qg automatic-rename off || exit 1
|
||||
|
||||
@@ -17,7 +17,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest$$"
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
DIR=$(mktemp -d)
|
||||
|
||||
@@ -19,7 +19,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest$$"
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
DIR=$(mktemp -d)
|
||||
|
||||
@@ -10,7 +10,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest$$"
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
OUT=$(mktemp)
|
||||
|
||||
@@ -10,7 +10,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest$$"
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
FIFO=$(mktemp -u)
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Trimming a blank source must leave a visible line for cursor reflow.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
export PATH TERM
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null
|
||||
}
|
||||
trap cleanup 0
|
||||
trap 'exit 1' 1 2 3 15
|
||||
|
||||
$TMUX new-session -d -x80 -y24 -s test 'sleep 100' || exit 1
|
||||
$TMUX set-option -g window-size manual || exit 1
|
||||
$TMUX new-window -t test:1 'sleep 100' || exit 1
|
||||
$TMUX resize-window -t test:0 -x20 -y24 || exit 1
|
||||
|
||||
# Exercise widening, narrowing, and equal widths with no source history.
|
||||
for pair in '0 1' '1 0' '1 2'; do
|
||||
set -- $pair
|
||||
if [ "$2" = 2 ]; then
|
||||
$TMUX new-window -t test:2 'sleep 100' || exit 1
|
||||
$TMUX resize-window -t test:2 -x80 -y24 || exit 1
|
||||
fi
|
||||
source=test:$1.0
|
||||
target=test:$2.0
|
||||
[ "$($TMUX display-message -p -t "$source" '#{history_size}')" = 0 ] ||
|
||||
fail "source has unexpected history"
|
||||
$TMUX copy-mode -s "$source" -t "$target" || exit 1
|
||||
$TMUX has-session -t test || fail "server died copying blank source"
|
||||
[ "$($TMUX display-message -p -t "$target" \
|
||||
'#{pane_in_mode} #{copy_cursor_x} #{copy_cursor_y}')" = '1 0 0' ] ||
|
||||
fail "blank source did not enter copy mode at the first cell"
|
||||
$TMUX send-keys -t "$target" -X cancel || exit 1
|
||||
done
|
||||
|
||||
# Real content still trims trailing empty lines, clamping the cursor to beta.
|
||||
$TMUX respawn-pane -k -t test:0.0 \
|
||||
"printf 'alpha\r\nbeta\r\n\r\n\r\n'; exec sleep 100" || exit 1
|
||||
i=0
|
||||
while [ "$($TMUX display-message -p -t test:0.0 '#{cursor_y}')" != 4 ]; do
|
||||
i=$((i + 1))
|
||||
[ "$i" -lt 10 ] || fail "source output did not arrive"
|
||||
sleep 1
|
||||
done
|
||||
$TMUX copy-mode -s test:0.0 -t test:1.0 || exit 1
|
||||
[ "$($TMUX display-message -p -t test:1.0 \
|
||||
'#{pane_in_mode} #{copy_cursor_y} #{copy_cursor_line}')" = '1 1 beta' ] ||
|
||||
fail "nonblank source content or trailing-line trimming changed"
|
||||
|
||||
exit 0
|
||||
@@ -153,13 +153,11 @@ wait_format "$p0" '#{pane_in_mode}' '0'
|
||||
wait_option @picked "$p1"
|
||||
|
||||
# Commands after display-panes run immediately while the mode remains.
|
||||
# Record the mode in the same command queue so client startup cannot race
|
||||
# the 500ms timeout.
|
||||
$TMUX set -g @after none || fail "set @after failed"
|
||||
$TMUX display-panes -Nd 500 -t "$p0" \; \
|
||||
set -gF -t "$p0" @after '#{pane_mode}' ||
|
||||
$TMUX display-panes -Nd 500 -t "$p0" \; set -g @after fast ||
|
||||
fail "display-panes immediate command failed"
|
||||
wait_option @after 'panes-mode'
|
||||
wait_option @after fast
|
||||
wait_format "$p0" '#{pane_mode}' 'panes-mode'
|
||||
wait_format "$p0" '#{pane_in_mode}' '0'
|
||||
|
||||
# Existing zoom is restored on exit.
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Damage-only redraws must evaluate both active and inactive border styles
|
||||
# for each client, even though the cached border cells belong to the pane.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
INNER="$TEST_TMUX -Lborder-style-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lborder-style-outer-$$ -f/dev/null"
|
||||
CAPTURE=$DIR/capture
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
wait_for_clients()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
count=$($INNER list-clients 2>/dev/null | wc -l)
|
||||
[ "$count" -eq 2 ] && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "two inner clients did not attach"
|
||||
}
|
||||
|
||||
wait_for_marker()
|
||||
{
|
||||
target=$1
|
||||
marker=$2
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t "$target" >"$CAPTURE" || exit 1
|
||||
grep -q "$marker" "$CAPTURE" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "client in $target did not receive $marker"
|
||||
}
|
||||
|
||||
cat >"$DIR/emitter.pl" <<'PERL'
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
$| = 1;
|
||||
for my $phase (1 .. 2) {
|
||||
while (!-e "$ENV{TRIGGER}-$phase") {
|
||||
select undef, undef, undef, 0.01;
|
||||
}
|
||||
# Change an unused palette entry and acknowledge it in the pane body.
|
||||
print "\e]4;200;rgb:11/22/0$phase\a\e[1;1HDAMAGE$phase";
|
||||
}
|
||||
sleep 100;
|
||||
PERL
|
||||
|
||||
$INNER new-session -d -s inner -x 60 -y 20 \
|
||||
"TRIGGER='$DIR/trigger' perl '$DIR/emitter.pl'" || exit 1
|
||||
$INNER set -g status off || exit 1
|
||||
$INNER set -g window-size manual || exit 1
|
||||
$INNER set -g automatic-rename off || exit 1
|
||||
$INNER set -g status-interval 0 || exit 1
|
||||
$INNER set -g pane-border-lines simple || exit 1
|
||||
$INNER set -g pane-border-status top || exit 1
|
||||
$INNER set -g pane-border-format 'CLIENT=#{client_name}' || exit 1
|
||||
FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x 35 -y 6 -X 5 -Y 5 \
|
||||
'sleep 100') || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 121 -y 20 'sleep 100' || exit 1
|
||||
$OUTER set -g status off || exit 1
|
||||
$OUTER set -g window-size manual || exit 1
|
||||
$OUTER set -g default-terminal screen || exit 1
|
||||
LEFT=$($OUTER display-message -p -t outer:0.0 '#{pane_id}') || exit 1
|
||||
RIGHT=$($OUTER split-window -h -PF '#{pane_id}' 'sleep 100') || exit 1
|
||||
for target in "$LEFT" "$RIGHT"; do
|
||||
$OUTER respawn-pane -k -t "$target" "$INNER attach-session -t inner" ||
|
||||
exit 1
|
||||
done
|
||||
wait_for_clients
|
||||
|
||||
NAME1=$($OUTER display-message -p -t "$LEFT" '#{pane_tty}') || exit 1
|
||||
NAME2=$($OUTER display-message -p -t "$RIGHT" '#{pane_tty}') || exit 1
|
||||
STYLE="fg=#{?#{==:#{client_name},$NAME1},red,blue}"
|
||||
$INNER set -g pane-border-style "$STYLE" || exit 1
|
||||
$INNER set -g pane-active-border-style "$STYLE" || exit 1
|
||||
|
||||
# Capture just the floating pane's rows, excluding the acknowledgement in
|
||||
# the tiled pane. Exercise each cache by changing the floating pane's focus.
|
||||
phase=1
|
||||
while [ "$phase" -le 2 ]; do
|
||||
if [ "$phase" -eq 2 ]; then
|
||||
$INNER select-pane -t "$FLOAT" || exit 1
|
||||
fi
|
||||
$INNER refresh-client -t "$NAME1" || exit 1
|
||||
$INNER refresh-client -t "$NAME2" || exit 1
|
||||
sleep 0.5
|
||||
for target in "$LEFT" "$RIGHT"; do
|
||||
$OUTER capture-pane -pe -S 5 -E 10 -t "$target" \
|
||||
>"$DIR/before-$target" || exit 1
|
||||
done
|
||||
RED=$(printf '\033[31m')
|
||||
BLUE=$(printf '\033[34m')
|
||||
grep -Fq "$RED" "$DIR/before-$LEFT" || fail "missing red border"
|
||||
grep -Fq "$BLUE" "$DIR/before-$RIGHT" || fail "missing blue border"
|
||||
|
||||
: >"$DIR/trigger-$phase"
|
||||
wait_for_marker "$LEFT" "DAMAGE$phase"
|
||||
wait_for_marker "$RIGHT" "DAMAGE$phase"
|
||||
sleep 0.2
|
||||
for target in "$LEFT" "$RIGHT"; do
|
||||
$OUTER capture-pane -pe -S 5 -E 10 -t "$target" \
|
||||
>"$CAPTURE" || exit 1
|
||||
diff -u "$DIR/before-$target" "$CAPTURE" ||
|
||||
fail "phase $phase changed $target's border style"
|
||||
done
|
||||
phase=$((phase + 1))
|
||||
done
|
||||
|
||||
exit 0
|
||||
@@ -1,108 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Regression test for a floating-pane drag bug: cmd_resize_pane_redraw_floating()
|
||||
# (cmd-resize-pane.c) reported damage for just a dragged floating pane's
|
||||
# content rectangle, not the one-cell border frame drawn around it (see the
|
||||
# "floating" case in screen-redraw.c, which draws that frame at
|
||||
# xoff-1/yoff-1 through xoff+sx/yoff+sy - one cell outside the pane's own
|
||||
# content area). Damage scoped to only the content area left the frame's
|
||||
# previous position undrawn as the pane moved, so dragging it left a trail
|
||||
# of un-erased border frames behind - visible as several "corners" stacked
|
||||
# up rather than just the pane's current one.
|
||||
#
|
||||
# This bug has nothing to do with images - it reproduces with a plain
|
||||
# floating pane and no image support required.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server >/dev/null 2>&1
|
||||
$TMUX2 kill-server >/dev/null 2>&1
|
||||
}
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
cleanup
|
||||
exit 1
|
||||
}
|
||||
|
||||
# drag STARTCOL STARTROW ENDCOL ENDROW
|
||||
#
|
||||
# Write a plain (unmodified) SGR button-1 press, drag update and release at
|
||||
# 1-based positions to the outer pane holding the inner client - this
|
||||
# matches the default MouseDown1Border/MouseDrag1Border bindings used to
|
||||
# move or resize a floating pane by its border.
|
||||
drag()
|
||||
{
|
||||
scol="$1"
|
||||
srow="$2"
|
||||
ecol="$3"
|
||||
erow="$4"
|
||||
|
||||
seq=$(printf '\033[<0;%s;%sM' "$scol" "$srow")
|
||||
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||
sleep 0.2
|
||||
seq=$(printf '\033[<32;%s;%sM' "$ecol" "$erow")
|
||||
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||
sleep 0.2
|
||||
seq=$(printf '\033[<0;%s;%sm' "$ecol" "$erow")
|
||||
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||
sleep 1
|
||||
}
|
||||
|
||||
cleanup
|
||||
|
||||
TMP=$(mktemp)
|
||||
trap "cleanup; rm -f $TMP" 0 1 15
|
||||
|
||||
$TMUX new-session -d -s inner -x 60 -y 20 'sh -c "sleep 100"' || exit 1
|
||||
$TMUX set -g mouse on
|
||||
$TMUX set -g default-command 'sh -c "sleep 100"'
|
||||
|
||||
FLOAT=$($TMUX new-pane -d -PF '#{pane_id}' -x 16 -y 5 -X 5 -Y 5) ||
|
||||
fail "new-pane -X -Y failed"
|
||||
FTOP=$($TMUX display-message -p -t "$FLOAT" '#{pane_top}')
|
||||
FLEFT=$($TMUX display-message -p -t "$FLOAT" '#{pane_left}')
|
||||
FWIDTH=$($TMUX display-message -p -t "$FLOAT" '#{pane_width}')
|
||||
|
||||
$TMUX2 new-session -d -x 60 -y 20 "$TMUX attach -t inner" || exit 1
|
||||
sleep 1
|
||||
OUTER=$($TMUX2 list-panes -F '#{pane_id}' | head -1)
|
||||
[ -n "$OUTER" ] || fail "No outer pane."
|
||||
|
||||
# Sanity check: exactly one floating pane, so exactly one top-left corner,
|
||||
# before dragging anything.
|
||||
$TMUX2 capturep -p -t "$OUTER" >$TMP || fail "capture failed"
|
||||
n=$(grep -o '┌' $TMP | wc -l)
|
||||
[ "$n" -eq 1 ] || fail "sanity: expected 1 corner before drag, found $n"
|
||||
|
||||
# Drag the floating pane by its top border (row FTOP-1, some column within
|
||||
# its width) down several rows in a few separate steps, then release. A
|
||||
# single drag() call already does press/motion/release, so call it several
|
||||
# times in a row to simulate a multi-step real drag.
|
||||
GRABCOL=$((FLEFT + FWIDTH / 2))
|
||||
STARTROW=$FTOP
|
||||
i=0
|
||||
while [ $i -lt 6 ]; do
|
||||
newrow=$((STARTROW + i + 1))
|
||||
drag $((GRABCOL + 1)) $((STARTROW + i)) $((GRABCOL + 1)) $newrow
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
$TMUX2 capturep -p -t "$OUTER" >$TMP || fail "capture failed"
|
||||
|
||||
# Exactly one top-left corner should remain - the pane's current position.
|
||||
# This is expected to fail before the fix: multiple corners (a trail of
|
||||
# un-erased frames) would remain from the intermediate drag positions.
|
||||
n=$(grep -o '┌' $TMP | wc -l)
|
||||
[ "$n" -eq 1 ] || fail "expected exactly 1 corner after drag, found $n (ghost frames left behind)"
|
||||
|
||||
exit 0
|
||||
@@ -1,88 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Each drag motion must draw the floating pane content once.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
cd "$DIR" || exit 1
|
||||
INNER="$TEST_TMUX -Ldoublecomp-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Ldoublecomp-outer-$$ -f/dev/null"
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
cd /
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
mouse()
|
||||
{
|
||||
sequence=$(printf '\033[<%s;%s;%s%s' "$1" "$2" "$3" "$4")
|
||||
$OUTER send-keys -t outer:0.0 -l "$sequence" || exit 1
|
||||
sleep 0.15
|
||||
}
|
||||
|
||||
$INNER new-session -d -s inner -x 40 -y 15 "printf '\\033[15;1HOUTSIDE'; exec sleep 100" || exit 1
|
||||
$INNER set-option -g status off || exit 1
|
||||
$INNER set-option -g window-size manual || exit 1
|
||||
$INNER set-option -g mouse on || exit 1
|
||||
$INNER set-option -g status-interval 0 || exit 1
|
||||
$INNER set-option -g automatic-rename off || exit 1
|
||||
FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x 15 -y 5 -X 5 -Y 2 \
|
||||
"printf 'DRAGMARK'; exec sleep 100") || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 40 -y 15 'sleep 100' || exit 1
|
||||
$OUTER set-option -g status off || exit 1
|
||||
$OUTER set-option -g window-size manual || exit 1
|
||||
$OUTER set-option -g default-terminal screen-256color || exit 1
|
||||
$OUTER respawn-pane -k -t outer:0.0 \
|
||||
"$TEST_TMUX -Ldoublecomp-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||
exit 1
|
||||
sleep 0.5
|
||||
|
||||
XOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||
YOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_top}')
|
||||
GRABCOL=$((XOFF + 3))
|
||||
BORDERROW=$YOFF
|
||||
|
||||
# Begin capture after focus changes from the initial mouse press have settled.
|
||||
mouse 0 "$GRABCOL" "$BORDERROW" M
|
||||
$OUTER pipe-pane -O -t outer:0.0 "cat >'$DIR/output'" || exit 1
|
||||
$INNER refresh-client || exit 1
|
||||
sleep 0.5
|
||||
grep -aq DRAGMARK "$DIR/output" || fail "capture missed floating pane content"
|
||||
grep -aq OUTSIDE "$DIR/output" || fail "capture missed untouched row"
|
||||
offset=$(wc -c <"$DIR/output")
|
||||
|
||||
i=0
|
||||
steps=6
|
||||
while [ "$i" -lt "$steps" ]; do
|
||||
GRABCOL=$((GRABCOL + 1))
|
||||
mouse 32 "$GRABCOL" "$BORDERROW" M
|
||||
i=$((i + 1))
|
||||
done
|
||||
mouse 0 "$GRABCOL" "$BORDERROW" m
|
||||
sleep 0.3
|
||||
|
||||
NEWXOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||
[ "$NEWXOFF" -eq "$((XOFF + steps))" ] || fail "floating pane did not move six columns"
|
||||
tail -c +"$((offset + 1))" "$DIR/output" >"$DIR/drag-output"
|
||||
n=$(perl -0777 -ne '$n = () = /DRAGMARK/g; print "$n\n"' "$DIR/drag-output")
|
||||
[ "$n" -eq "$steps" ] ||
|
||||
fail "$steps drag motions wrote the floating pane content $n times"
|
||||
|
||||
exit 0
|
||||
@@ -1,159 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Regression test: dragging a floating pane across another pane's ordinary
|
||||
# content must not redraw that other pane's scrollbar, unless the drag
|
||||
# actually crosses the scrollbar's own strip.
|
||||
#
|
||||
# cmd_resize_pane_redraw_floating() (cmd-resize-pane.c) used to flag
|
||||
# PANE_REDRAWSCROLLBAR on any pane whose whole *body* intersected the
|
||||
# floating pane's old or new rectangle, rather than just its narrow
|
||||
# scrollbar strip - so dragging a floating pane back and forth over an
|
||||
# ordinary tiled pane's content (never touching its scrollbar) still
|
||||
# needlessly redrew that pane's scrollbar on every motion step. See
|
||||
# tmux-image-redraw-known-bugs.md for the full write-up.
|
||||
#
|
||||
# This is checked by giving the non-dragged pane a distinctive scrollbar
|
||||
# colour and counting how many times its SGR code appears in the client's
|
||||
# raw output while the floating pane is dragged vertically over that pane's
|
||||
# body, well clear of its scrollbar column: with the fix, it should never
|
||||
# reappear after the initial draw.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server >/dev/null 2>&1
|
||||
$TMUX2 kill-server >/dev/null 2>&1
|
||||
}
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
cleanup
|
||||
exit 1
|
||||
}
|
||||
|
||||
# drag STARTCOL STARTROW ENDCOL ENDROW
|
||||
drag()
|
||||
{
|
||||
scol="$1"
|
||||
srow="$2"
|
||||
ecol="$3"
|
||||
erow="$4"
|
||||
|
||||
seq=$(printf '\033[<0;%s;%sM' "$scol" "$srow")
|
||||
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||
sleep 0.2
|
||||
seq=$(printf '\033[<32;%s;%sM' "$ecol" "$erow")
|
||||
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||
sleep 0.2
|
||||
seq=$(printf '\033[<0;%s;%sm' "$ecol" "$erow")
|
||||
$TMUX2 send-keys -t "$OUTER" -l "$seq" 2>/dev/null
|
||||
sleep 0.5
|
||||
}
|
||||
|
||||
cleanup
|
||||
|
||||
TMP=$(mktemp)
|
||||
trap "cleanup; rm -f $TMP" 0 1 15
|
||||
|
||||
$TMUX new-session -d -s inner -x 60 -y 20 'sh -c "sleep 100"' || exit 1
|
||||
$TMUX set -g mouse on || fail "set mouse failed"
|
||||
$TMUX set -g default-command 'sh -c "sleep 100"' || fail "set default-command failed"
|
||||
$TMUX set -g pane-scrollbars on || fail "set pane-scrollbars failed"
|
||||
|
||||
$TMUX split-window -h -t inner 'sh -c "sleep 100"' || fail "split-window failed"
|
||||
|
||||
PANES=$($TMUX list-panes -t inner -F '#{pane_id} #{pane_left}')
|
||||
LEFT=$(echo "$PANES" | sort -k2 -n | head -1 | cut -d' ' -f1)
|
||||
[ -n "$LEFT" ] || fail "could not identify left pane"
|
||||
|
||||
# A distinctive scrollbar colour for the non-dragged (left) pane only.
|
||||
$TMUX set -p -t "$LEFT" pane-scrollbars-style 'fg=colour201,bg=colour17' ||
|
||||
fail "set pane-scrollbars-style failed"
|
||||
|
||||
ALEFT=$($TMUX display-message -p -t "$LEFT" '#{pane_left}')
|
||||
ATOP=$($TMUX display-message -p -t "$LEFT" '#{pane_top}')
|
||||
AWIDTH=$($TMUX display-message -p -t "$LEFT" '#{pane_width}')
|
||||
AHEIGHT=$($TMUX display-message -p -t "$LEFT" '#{pane_height}')
|
||||
[ "$AWIDTH" -gt 15 ] || fail "left pane too narrow for this test ($AWIDTH)"
|
||||
|
||||
# A small floating pane placed well inside the left pane's content area,
|
||||
# clear of its (right-hand) scrollbar column by several columns.
|
||||
FLOAT=$($TMUX new-pane -d -PF '#{pane_id}' -x 8 -y 5 \
|
||||
-X $((ALEFT + 2)) -Y $((ATOP + 2))) || fail "new-pane -X -Y failed"
|
||||
FTOP=$($TMUX display-message -p -t "$FLOAT" '#{pane_top}')
|
||||
FLEFT=$($TMUX display-message -p -t "$FLOAT" '#{pane_left}')
|
||||
FWIDTH=$($TMUX display-message -p -t "$FLOAT" '#{pane_width}')
|
||||
[ $((FLEFT + FWIDTH + 3)) -lt $((ALEFT + AWIDTH)) ] ||
|
||||
fail "sanity: floating pane too close to the scrollbar column"
|
||||
|
||||
# Start the outer session with a plain shell, then start capturing before
|
||||
# triggering the attach - starting the attach as the outer pane's initial
|
||||
# command would mean pipe-pane only starts after the attach-driven initial
|
||||
# redraw (which draws the scrollbars) has already happened, missing it.
|
||||
$TMUX2 new-session -d -x 60 -y 20 || exit 1
|
||||
OUTER=$($TMUX2 list-panes -F '#{pane_id}' | head -1)
|
||||
[ -n "$OUTER" ] || fail "No outer pane."
|
||||
$TMUX2 pipe-pane -t "$OUTER" -O "cat >$TMP" || fail "pipe-pane failed"
|
||||
$TMUX2 send-keys -t "$OUTER" -l "$TMUX attach -t inner" || fail "send attach failed"
|
||||
$TMUX2 send-keys -t "$OUTER" Enter || fail "send enter failed"
|
||||
sleep 1
|
||||
|
||||
# Sanity check: the distinctive scrollbar colour reaches the client at all.
|
||||
grep -qa '48;5;201' $TMP || fail "sanity: scrollbar colour never reached the client"
|
||||
: >$TMP
|
||||
|
||||
# Drag the floating pane straight up and down by its top border, staying at
|
||||
# a fixed column the whole time - this never crosses the left pane's
|
||||
# scrollbar strip, only its ordinary content.
|
||||
GRABCOL=$((FLEFT + FWIDTH / 2))
|
||||
row=$FTOP
|
||||
i=0
|
||||
while [ $i -lt 6 ]; do
|
||||
newrow=$((row + 1))
|
||||
drag $GRABCOL $row $GRABCOL $newrow
|
||||
row=$newrow
|
||||
i=$((i + 1))
|
||||
done
|
||||
i=0
|
||||
while [ $i -lt 6 ]; do
|
||||
newrow=$((row - 1))
|
||||
drag $GRABCOL $row $GRABCOL $newrow
|
||||
row=$newrow
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
# The scrollbar colour should never reappear - its geometry never changed,
|
||||
# and the drag never crossed its column. This is expected to fail before
|
||||
# the fix - see the header comment.
|
||||
n=$(grep -ac '48;5;201' $TMP)
|
||||
[ "$n" -eq 0 ] ||
|
||||
fail "left pane's scrollbar was redrawn $n times while dragging over its body only"
|
||||
|
||||
# Cross the scrollbar and return, comparing each damaged redraw with a full
|
||||
# refresh to check that covering and uncovering its cells leaves no artifacts.
|
||||
offset=$((ALEFT + AWIDTH - FLEFT - FWIDTH / 2))
|
||||
for delta in "$offset" "$((-offset))"; do
|
||||
oldleft=$($TMUX display-message -p -t "$FLOAT" '#{pane_left}')
|
||||
newcol=$((GRABCOL + delta))
|
||||
drag "$GRABCOL" "$row" "$newcol" "$row"
|
||||
GRABCOL=$newcol
|
||||
newleft=$($TMUX display-message -p -t "$FLOAT" '#{pane_left}')
|
||||
[ "$newleft" -eq "$((oldleft + delta))" ] ||
|
||||
fail "floating pane did not move across the scrollbar as expected"
|
||||
before=$($TMUX2 capture-pane -pe -t "$OUTER" -S 0 -E "$((AHEIGHT - 1))")
|
||||
$TMUX refresh-client || fail "refresh-client failed"
|
||||
sleep 0.5
|
||||
after=$($TMUX2 capture-pane -pe -t "$OUTER" -S 0 -E "$((AHEIGHT - 1))")
|
||||
[ "$before" = "$after" ] ||
|
||||
fail "scrollbar crossing differs from a full redraw"
|
||||
done
|
||||
|
||||
exit 0
|
||||
@@ -1,93 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Synchronized drag output must not cause a full redraw of untouched rows.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
cd "$DIR" || exit 1
|
||||
INNER="$TEST_TMUX -Lsyncdefer-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lsyncdefer-outer-$$ -f/dev/null"
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
cd /
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
mouse()
|
||||
{
|
||||
sequence=$(printf '\033[<%s;%s;%s%s' "$1" "$2" "$3" "$4")
|
||||
$OUTER send-keys -t outer:0.0 -l "$sequence" || exit 1
|
||||
sleep 0.15
|
||||
}
|
||||
|
||||
$INNER new-session -d -s inner -x 40 -y 15 "printf '\\033[15;1HOUTSIDE'; exec sleep 100" || exit 1
|
||||
$INNER set-option -g status off || exit 1
|
||||
$INNER set-option -g window-size manual || exit 1
|
||||
$INNER set-option -g mouse on || exit 1
|
||||
$INNER set-option -g status-interval 0 || exit 1
|
||||
$INNER set-option -g automatic-rename off || exit 1
|
||||
FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x 15 -y 5 -X 5 -Y 2 \
|
||||
"printf 'DRAGMARK'; exec sleep 100") || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 40 -y 15 'sleep 100' || exit 1
|
||||
$OUTER set-option -g status off || exit 1
|
||||
$OUTER set-option -g window-size manual || exit 1
|
||||
$OUTER set-option -g default-terminal screen-256color || exit 1
|
||||
$INNER set-option -as terminal-features ',screen-256color:sync' || exit 1
|
||||
$OUTER respawn-pane -k -t outer:0.0 \
|
||||
"$TEST_TMUX -Lsyncdefer-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||
exit 1
|
||||
sleep 0.5
|
||||
|
||||
XOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||
YOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_top}')
|
||||
GRABCOL=$((XOFF + 3))
|
||||
BORDERROW=$YOFF
|
||||
|
||||
# Begin capture after focus changes from the initial mouse press have settled.
|
||||
mouse 0 "$GRABCOL" "$BORDERROW" M
|
||||
$OUTER pipe-pane -O -t outer:0.0 "cat >'$DIR/output'" || exit 1
|
||||
$INNER refresh-client || exit 1
|
||||
sleep 0.5
|
||||
grep -aq DRAGMARK "$DIR/output" || fail "capture missed floating pane content"
|
||||
grep -aq OUTSIDE "$DIR/output" || fail "capture missed untouched row"
|
||||
offset=$(wc -c <"$DIR/output")
|
||||
|
||||
i=0
|
||||
steps=6
|
||||
while [ "$i" -lt "$steps" ]; do
|
||||
GRABCOL=$((GRABCOL + 1))
|
||||
mouse 32 "$GRABCOL" "$BORDERROW" M
|
||||
i=$((i + 1))
|
||||
done
|
||||
mouse 0 "$GRABCOL" "$BORDERROW" m
|
||||
sleep 0.3
|
||||
|
||||
NEWXOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||
[ "$NEWXOFF" -eq "$((XOFF + steps))" ] || fail "floating pane did not move six columns"
|
||||
tail -c +"$((offset + 1))" "$DIR/output" >"$DIR/drag-output"
|
||||
n=$(perl -0777 -ne '$n = () = /DRAGMARK/g; print "$n\n"' "$DIR/drag-output")
|
||||
[ "$n" -ge "$steps" ] || fail "drag output did not reach the client"
|
||||
perl -0777 -ne 'exit(/\e\[\?2026h/ ? 0 : 1)' "$DIR/drag-output" ||
|
||||
fail "drag did not use synchronized output"
|
||||
if grep -aq OUTSIDE "$DIR/drag-output"; then
|
||||
fail "synchronized drag redrew an untouched row"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -1,206 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Mirror of floating-pane-drag-wide-character.sh for the *right* edge of a
|
||||
# damage rectangle. redraw_damage_grow_span_clip() (screen-redraw.c) widens
|
||||
# a damage rectangle's right edge by one cell whenever it isn't already at
|
||||
# the span's own edge, to pull in a wide character's base half when the
|
||||
# edge lands on its padding half - but it did this unconditionally, with no
|
||||
# check of which half it was actually touching. When the edge instead
|
||||
# already lands cleanly on a fresh character's base cell (a character fully
|
||||
# outside the range), growing right pulls in just that base cell -
|
||||
# tty_draw_line() sees it can't fit that character's full width in the
|
||||
# remaining range (tty_draw_line_get_empty()'s gc->data.width > nx check)
|
||||
# and clears it, exactly as the left-edge bug cleared a neighbouring
|
||||
# character's padding half.
|
||||
#
|
||||
# As with the left-edge test, the exact column parity needs to be
|
||||
# deterministic to actually catch it. This constructs it by creating the
|
||||
# floating pane, checking its real position, and recreating it one column
|
||||
# over if necessary until the vacated rectangle's right edge lands on a
|
||||
# base cell.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
INNER="$TEST_TMUX -Lwidecharr-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lwidecharr-outer-$$ -f/dev/null"
|
||||
EMITTER=$DIR/emitter.pl
|
||||
BASE=$DIR/base
|
||||
CAPTURE=$DIR/capture
|
||||
FLOAT=
|
||||
PANEWIDTH=12
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
[ -s "$CAPTURE" ] && cat "$CAPTURE" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
wait_for_client()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
CLIENT=$($INNER list-clients -F '#{client_name}' 2>/dev/null)
|
||||
[ -n "$CLIENT" ] && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "inner client did not attach"
|
||||
}
|
||||
|
||||
wait_outer_has()
|
||||
{
|
||||
marker=$1
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||
grep -q "$marker" "$CAPTURE" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "outer client did not show $marker"
|
||||
}
|
||||
|
||||
slice_columns()
|
||||
{
|
||||
# Extract terminal columns [COL1, COL2) from lines [ROW1, ROW2] of
|
||||
# $1, decoding UTF-8 - the pane's own new position (well clear of
|
||||
# this range) must not affect the comparison, so this only looks at
|
||||
# the narrow strip actually vacated, not the whole line. capture-pane
|
||||
# text has one decoded character per double-width cell pair (every
|
||||
# character here is width 2), so terminal columns are converted to
|
||||
# character indices by halving before slicing.
|
||||
perl -CSD -e '
|
||||
my ($row1, $row2, $col1, $col2, $file) = @ARGV;
|
||||
open my $fh, "<:encoding(UTF-8)", $file or die $!;
|
||||
my @lines = <$fh>;
|
||||
my $c1 = int($col1 / 2);
|
||||
my $c2 = int(($col2 + 1) / 2);
|
||||
for my $n ($row1 .. $row2) {
|
||||
my $line = $lines[$n - 1];
|
||||
$line =~ s/\R\z//;
|
||||
print substr($line, $c1, $c2 - $c1), "\n";
|
||||
}
|
||||
' "$ROW1" "$ROW2" "$COL1" "$COL2" "$1"
|
||||
}
|
||||
|
||||
wait_old_rows_restored()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||
slice_columns "$BASE" >"$DIR/want"
|
||||
slice_columns "$CAPTURE" >"$DIR/got"
|
||||
cmp -s "$DIR/want" "$DIR/got" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "wide characters under the floating pane's vacated right edge were not restored"
|
||||
}
|
||||
|
||||
mouse()
|
||||
{
|
||||
sequence=$(printf '\033[<%s;%s;%s%s' "$1" "$2" "$3" "$4")
|
||||
$OUTER send-keys -t outer:0.0 -l "$sequence" || exit 1
|
||||
sleep 0.1
|
||||
}
|
||||
|
||||
cat >"$EMITTER" <<'PERL'
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
binmode STDOUT, ':encoding(UTF-8)';
|
||||
$| = 1;
|
||||
for my $row (1 .. 10) {
|
||||
print "\e[$row;1H", chr(0x754c) x 20;
|
||||
}
|
||||
sleep 100;
|
||||
PERL
|
||||
|
||||
$INNER new-session -d -s inner -x 40 -y 10 "perl '$EMITTER'" || exit 1
|
||||
$INNER set-option -g status off || exit 1
|
||||
$INNER set-option -g window-size manual || exit 1
|
||||
$INNER set-option -g mouse on || exit 1
|
||||
$INNER set-option -g pane-scrollbars off || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 40 -y 10 'sleep 100' || exit 1
|
||||
$OUTER set-option -g status off || exit 1
|
||||
$OUTER set-option -g window-size manual || exit 1
|
||||
$OUTER set-option -g default-terminal screen-256color || exit 1
|
||||
$OUTER respawn-pane -k -t outer:0.0 \
|
||||
"$TEST_TMUX -Lwidecharr-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||
exit 1
|
||||
|
||||
wait_for_client
|
||||
wait_outer_has '界界界'
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$BASE" || exit 1
|
||||
|
||||
# Create the floating pane, then check its actual resulting position. Try
|
||||
# adjacent starting columns until the vacated rectangle's right edge
|
||||
# (xoff + PANEWIDTH + 1, the border-grown exclusive end - see
|
||||
# window_pane_damage_floating(), window.c) lands on an even (base-cell)
|
||||
# column.
|
||||
startx=5
|
||||
tries=0
|
||||
while [ "$tries" -lt 2 ]; do
|
||||
[ -n "$FLOAT" ] && $INNER kill-pane -t "$FLOAT" 2>/dev/null
|
||||
FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x "$PANEWIDTH" -y 3 \
|
||||
-X "$startx" -Y 5 \
|
||||
'sh -c "printf FLOATMARK; exec sleep 100"') || exit 1
|
||||
sleep 0.2
|
||||
XOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||
YOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_top}')
|
||||
oldright=$((XOFF + PANEWIDTH + 1))
|
||||
if [ $((oldright % 2)) -eq 0 ]; then
|
||||
break
|
||||
fi
|
||||
startx=$((startx + 1))
|
||||
tries=$((tries + 1))
|
||||
done
|
||||
[ $(((XOFF + PANEWIDTH + 1) % 2)) -eq 0 ] ||
|
||||
fail "could not find bad-parity starting column"
|
||||
|
||||
wait_outer_has FLOATMARK
|
||||
|
||||
ROW1=$((YOFF + 1))
|
||||
ROW2=$((YOFF + 3))
|
||||
COL1=$((XOFF + PANEWIDTH - 2))
|
||||
COL2=$((XOFF + PANEWIDTH + 2))
|
||||
|
||||
# Grab the pane's top border a couple of columns in (avoiding the corner
|
||||
# cells) and drag it well clear of its old rectangle - far enough right
|
||||
# that its new position starts past the checked columns above (which sit
|
||||
# just past the pane's *original* right edge).
|
||||
GRABCOL=$((XOFF + 3))
|
||||
BORDERROW=$((YOFF))
|
||||
|
||||
seq=$(printf '\033[<0;%s;%sM' "$GRABCOL" "$BORDERROW")
|
||||
$OUTER send-keys -t outer:0.0 -l "$seq" || exit 1
|
||||
sleep 0.1
|
||||
seq=$(printf '\033[<32;%s;%sM' "$((GRABCOL + 20))" "$BORDERROW")
|
||||
$OUTER send-keys -t outer:0.0 -l "$seq" || exit 1
|
||||
sleep 0.1
|
||||
seq=$(printf '\033[<0;%s;%sm' "$((GRABCOL + 20))" "$BORDERROW")
|
||||
$OUTER send-keys -t outer:0.0 -l "$seq" || exit 1
|
||||
sleep 0.1
|
||||
|
||||
NEWXOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||
[ "$NEWXOFF" != "$XOFF" ] || fail "sanity: floating pane did not move (still at $XOFF)"
|
||||
|
||||
wait_old_rows_restored
|
||||
|
||||
exit 0
|
||||
@@ -1,200 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Damage at a floating pane's vacated edge must always redraw a complete
|
||||
# grid character. redraw_damage_grow_span_clip() (screen-redraw.c) widens a
|
||||
# damage rectangle's left edge by one cell whenever it isn't already at the
|
||||
# span's own edge, to pull in a wide character's base half when the edge
|
||||
# lands on its padding half - but it did this unconditionally, with no
|
||||
# check of which half it was actually touching. When the edge instead
|
||||
# already lands cleanly on a fresh character's base cell, growing left
|
||||
# walks into the *previous*, unrelated character's padding cell and blanks
|
||||
# it (tty_draw_line() treats any leading padding cell as proof its own
|
||||
# range starts mid-character).
|
||||
#
|
||||
# This is a general damage-composition bug, not specific to any one kind of
|
||||
# pane, but the exact column parity needs to be deterministic to actually
|
||||
# catch it (a lucky parity draws fine). This constructs it by creating the
|
||||
# floating pane, checking its real position (the border-framing offset
|
||||
# added to -X is not something to hand-compute), and recreating it one
|
||||
# column over if necessary until the vacated rectangle's left edge lands on
|
||||
# a base cell.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
INNER="$TEST_TMUX -Lwidechar-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lwidechar-outer-$$ -f/dev/null"
|
||||
EMITTER=$DIR/emitter.pl
|
||||
BASE=$DIR/base
|
||||
CAPTURE=$DIR/capture
|
||||
FLOAT=
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
[ -s "$CAPTURE" ] && cat "$CAPTURE" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
wait_for_client()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
CLIENT=$($INNER list-clients -F '#{client_name}' 2>/dev/null)
|
||||
[ -n "$CLIENT" ] && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "inner client did not attach"
|
||||
}
|
||||
|
||||
wait_outer_has()
|
||||
{
|
||||
marker=$1
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||
grep -q "$marker" "$CAPTURE" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "outer client did not show $marker"
|
||||
}
|
||||
|
||||
slice_columns()
|
||||
{
|
||||
# Extract terminal columns [COL1, COL2) from lines [ROW1, ROW2] of
|
||||
# $1, decoding UTF-8 - the pane's own new position (well clear of
|
||||
# this range) must not affect the comparison, so this only looks at
|
||||
# the narrow strip actually vacated, not the whole line. capture-pane
|
||||
# text has one decoded character per double-width cell pair (every
|
||||
# character here is width 2), so terminal columns are converted to
|
||||
# character indices by halving before slicing.
|
||||
perl -CSD -e '
|
||||
my ($row1, $row2, $col1, $col2, $file) = @ARGV;
|
||||
open my $fh, "<:encoding(UTF-8)", $file or die $!;
|
||||
my @lines = <$fh>;
|
||||
my $c1 = int($col1 / 2);
|
||||
my $c2 = int(($col2 + 1) / 2);
|
||||
for my $n ($row1 .. $row2) {
|
||||
my $line = $lines[$n - 1];
|
||||
$line =~ s/\R\z//;
|
||||
print substr($line, $c1, $c2 - $c1), "\n";
|
||||
}
|
||||
' "$ROW1" "$ROW2" "$COL1" "$COL2" "$1"
|
||||
}
|
||||
|
||||
wait_old_rows_restored()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||
slice_columns "$BASE" >"$DIR/want"
|
||||
slice_columns "$CAPTURE" >"$DIR/got"
|
||||
cmp -s "$DIR/want" "$DIR/got" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "wide characters under the floating pane's vacated edge were not restored"
|
||||
}
|
||||
|
||||
mouse()
|
||||
{
|
||||
sequence=$(printf '\033[<%s;%s;%s%s' "$1" "$2" "$3" "$4")
|
||||
$OUTER send-keys -t outer:0.0 -l "$sequence" || exit 1
|
||||
sleep 0.1
|
||||
}
|
||||
|
||||
cat >"$EMITTER" <<'PERL'
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
binmode STDOUT, ':encoding(UTF-8)';
|
||||
$| = 1;
|
||||
for my $row (1 .. 10) {
|
||||
print "\e[$row;1H", chr(0x754c) x 20;
|
||||
}
|
||||
sleep 100;
|
||||
PERL
|
||||
|
||||
$INNER new-session -d -s inner -x 40 -y 10 "perl '$EMITTER'" || exit 1
|
||||
$INNER set-option -g status off || exit 1
|
||||
$INNER set-option -g window-size manual || exit 1
|
||||
$INNER set-option -g mouse on || exit 1
|
||||
$INNER set-option -g pane-scrollbars off || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 40 -y 10 'sleep 100' || exit 1
|
||||
$OUTER set-option -g status off || exit 1
|
||||
$OUTER set-option -g window-size manual || exit 1
|
||||
$OUTER set-option -g default-terminal screen-256color || exit 1
|
||||
$OUTER respawn-pane -k -t outer:0.0 \
|
||||
"$TEST_TMUX -Lwidechar-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||
exit 1
|
||||
|
||||
wait_for_client
|
||||
wait_outer_has '界界界'
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$BASE" || exit 1
|
||||
|
||||
# Create the floating pane, then check its actual resulting position. Try
|
||||
# adjacent starting columns until the vacated rectangle's left edge
|
||||
# (xoff - 1) lands on an even (base-cell) column - the odd case is the one
|
||||
# every earlier manual test happened to land on by chance.
|
||||
startx=5
|
||||
tries=0
|
||||
while [ "$tries" -lt 2 ]; do
|
||||
[ -n "$FLOAT" ] && $INNER kill-pane -t "$FLOAT" 2>/dev/null
|
||||
FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x 12 -y 3 -X "$startx" \
|
||||
-Y 5 'sh -c "printf FLOATMARK; exec sleep 100"') || exit 1
|
||||
sleep 0.2
|
||||
XOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||
YOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_top}')
|
||||
oldleft=$((XOFF - 1))
|
||||
if [ $((oldleft % 2)) -eq 0 ]; then
|
||||
break
|
||||
fi
|
||||
startx=$((startx + 1))
|
||||
tries=$((tries + 1))
|
||||
done
|
||||
[ $(((XOFF - 1) % 2)) -eq 0 ] || fail "could not find bad-parity starting column"
|
||||
|
||||
wait_outer_has FLOATMARK
|
||||
|
||||
ROW1=$((YOFF + 1))
|
||||
ROW2=$((YOFF + 3))
|
||||
COL1=$((XOFF - 4))
|
||||
COL2=$((XOFF + 4))
|
||||
|
||||
# Grab the pane's top border a couple of columns in (avoiding the corner
|
||||
# cells) and drag it well clear of its old rectangle.
|
||||
GRABCOL=$((XOFF + 3))
|
||||
BORDERROW=$((YOFF))
|
||||
|
||||
seq=$(printf '\033[<0;%s;%sM' "$GRABCOL" "$BORDERROW")
|
||||
$OUTER send-keys -t outer:0.0 -l "$seq" || exit 1
|
||||
sleep 0.1
|
||||
seq=$(printf '\033[<32;%s;%sM' "$((GRABCOL + 15))" "$BORDERROW")
|
||||
$OUTER send-keys -t outer:0.0 -l "$seq" || exit 1
|
||||
sleep 0.1
|
||||
seq=$(printf '\033[<0;%s;%sm' "$((GRABCOL + 15))" "$BORDERROW")
|
||||
$OUTER send-keys -t outer:0.0 -l "$seq" || exit 1
|
||||
sleep 0.1
|
||||
|
||||
NEWXOFF=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||
[ "$NEWXOFF" != "$XOFF" ] || fail "sanity: floating pane did not move (still at $XOFF)"
|
||||
|
||||
wait_old_rows_restored
|
||||
|
||||
exit 0
|
||||
@@ -1,102 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Returning from the alternate screen must redraw the visible part of a
|
||||
# floating pane clipped at the left edge, the top edge, or both.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
INNER="$TEST_TMUX -Loffscreen-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Loffscreen-outer-$$ -f/dev/null"
|
||||
EMITTER=$DIR/emitter.pl
|
||||
CAPTURE=$DIR/capture
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
[ -s "$CAPTURE" ] && cat "$CAPTURE" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
wait_outer_has()
|
||||
{
|
||||
marker=$1
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||
grep -q "$marker" "$CAPTURE" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "outer client did not show $marker"
|
||||
}
|
||||
|
||||
cat >"$EMITTER" <<'PERL'
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
$| = 1;
|
||||
for my $row (1 .. 5) {
|
||||
print "\e[$row;1H", 'A' x 15;
|
||||
}
|
||||
while (!-e "$ENV{TRIGGER}-alternate") {
|
||||
select undef, undef, undef, 0.01;
|
||||
}
|
||||
print "\e[?1049h";
|
||||
for my $row (1 .. 5) {
|
||||
print "\e[$row;1H", 'B' x 15;
|
||||
}
|
||||
while (!-e "$ENV{TRIGGER}-restore") {
|
||||
select undef, undef, undef, 0.01;
|
||||
}
|
||||
print "\e[?1049l";
|
||||
sleep 100;
|
||||
PERL
|
||||
|
||||
$INNER new-session -d -s inner -x 40 -y 10 'sleep 100' || exit 1
|
||||
$INNER set-option -g status off || exit 1
|
||||
$INNER set-option -g window-size manual || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 40 -y 10 'sleep 100' || exit 1
|
||||
$OUTER set-option -g status off || exit 1
|
||||
$OUTER set-option -g window-size manual || exit 1
|
||||
$OUTER set-option -g default-terminal screen-256color || exit 1
|
||||
$OUTER respawn-pane -k -t outer:0.0 \
|
||||
"$TEST_TMUX -Loffscreen-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||
exit 1
|
||||
|
||||
for position in left top both; do
|
||||
case "$position" in
|
||||
left) x=-5; y=2 ;;
|
||||
top) x=5; y=-2 ;;
|
||||
both) x=-5; y=-2 ;;
|
||||
esac
|
||||
FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x 15 -y 5 -X "$x" -Y "$y" \
|
||||
"TRIGGER='$DIR/$position' perl '$EMITTER'") || exit 1
|
||||
[ "$($INNER display-message -p -t "$FLOAT" '#{pane_left},#{pane_top}')" = "$((x + 1)),$((y + 1))" ] ||
|
||||
fail "$position: floating pane has unexpected position"
|
||||
wait_outer_has AAAAA
|
||||
cp "$CAPTURE" "$DIR/primary"
|
||||
: >"$DIR/$position-alternate"
|
||||
wait_outer_has BBBBB
|
||||
: >"$DIR/$position-restore"
|
||||
wait_outer_has AAAAA
|
||||
cmp -s "$DIR/primary" "$CAPTURE" ||
|
||||
fail "$position: primary screen was not completely restored"
|
||||
$INNER kill-pane -t "$FLOAT" || exit 1
|
||||
done
|
||||
|
||||
exit 0
|
||||
@@ -1,179 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# A damage rectangle's clip range is grown to avoid splitting a wide
|
||||
# character, but redraw_damage_grow_span_clip() (screen-redraw.c) only ever
|
||||
# checks the span's own pane *content* grid (wp->screen) for that. For a
|
||||
# REDRAW_SPAN_PANE span, that same range is then also handed to
|
||||
# redraw_damage_draw_pane_prompt() to recompose the pane's separately
|
||||
# rendered prompt (wp->prompt, e.g. from "command-prompt -P") over the
|
||||
# damaged sub-range - but the prompt is drawn into its own, freshly
|
||||
# allocated one-line screen, unrelated to the pane's content grid, so a
|
||||
# range grown (or left ungrown) against the content is not necessarily
|
||||
# grown correctly for the prompt's own wide characters.
|
||||
#
|
||||
# This is invisible when the pane's own content is plain ASCII (as here):
|
||||
# redraw_damage_grow_span_clip() never finds anything to grow against, so
|
||||
# the raw, ungrown geometric range is passed straight through to the
|
||||
# prompt - and if that range's edge lands mid-character in the *prompt's*
|
||||
# grid, tty_draw_line() clears the character it cuts through
|
||||
# (tty_draw_line_get_empty()'s gc->data.width > nx check, for a trailing
|
||||
# base cell with no room left for its padding half).
|
||||
#
|
||||
# The trigger is a palette change (OSC 4) in a tiled pane that is one half
|
||||
# of a vertical split running the full height of the window - occluded
|
||||
# under the floating pane, but still geometrically triggering a redraw of
|
||||
# its own rectangle. Positioned so the split boundary falls inside the
|
||||
# floating pane's own CJK prompt, this reproduces exactly Codex's report:
|
||||
# "a floating pane containing a CJK prompt across a tiled-pane boundary -
|
||||
# a palette update in the tiled pane blanks a prompt character."
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
cd "$DIR" || exit 1
|
||||
INNER="$TEST_TMUX -Lpromptwide-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lpromptwide-outer-$$ -f/dev/null"
|
||||
EMITTER=$DIR/emitter.pl
|
||||
CAPTURE=$DIR/capture
|
||||
FLOAT=
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
[ -s "$CAPTURE" ] && cat "$CAPTURE" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
cd /
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
wait_for_client()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
CLIENT=$($INNER list-clients -F '#{client_name}' 2>/dev/null)
|
||||
[ -n "$CLIENT" ] && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "inner client did not attach"
|
||||
}
|
||||
|
||||
wait_outer_has()
|
||||
{
|
||||
marker=$1
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||
grep -q "$marker" "$CAPTURE" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "outer client did not show $marker"
|
||||
}
|
||||
|
||||
wait_prompt_row_intact()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||
line=$(sed -n "${PROMPTROW}p" "$CAPTURE")
|
||||
case $line in
|
||||
*"$PROMPTTEXT"*) return 0 ;;
|
||||
esac
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "the CJK prompt was not intact after the palette-triggered damage - got: $line"
|
||||
}
|
||||
|
||||
cat >"$EMITTER" <<'PERL'
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
$| = 1;
|
||||
my $line = <STDIN>;
|
||||
print "\e]4;1;rgb:11/22/33\e\\";
|
||||
sleep 100;
|
||||
PERL
|
||||
|
||||
$INNER new-session -d -s inner -x 60 -y 12 "perl '$EMITTER'" || exit 1
|
||||
$INNER set-option -g status off || exit 1
|
||||
$INNER set-option -g window-size manual || exit 1
|
||||
LEFT=$($INNER list-panes -t inner -F '#{pane_id}') || exit 1
|
||||
|
||||
# Split so the boundary between the two tiled panes falls at column 17 -
|
||||
# used below to pick a floating-pane column that lands the boundary
|
||||
# mid-character inside the prompt.
|
||||
RIGHT=$($INNER split-window -t inner -h -l 43 -PF '#{pane_id}' \
|
||||
'sleep 100') || exit 1
|
||||
RX=$($INNER display-message -p -t "$RIGHT" '#{pane_left}') || exit 1
|
||||
|
||||
# Create the floating pane, then check its actual resulting position (the
|
||||
# border-framing offset added to -X is not something to hand-compute). Try
|
||||
# adjacent starting columns until the split boundary lands on an odd
|
||||
# (padding-half) column of the prompt's own numbering, and within the
|
||||
# prompt's 12-column width.
|
||||
startx=10
|
||||
tries=0
|
||||
while [ "$tries" -lt 4 ]; do
|
||||
[ -n "$FLOAT" ] && $INNER kill-pane -t "$FLOAT" 2>/dev/null
|
||||
FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x 24 -y 5 -X "$startx" \
|
||||
-Y 2 "sh -c 'i=0; while [ \$i -lt 10 ]; do \
|
||||
printf AAAAAAAAAAAAAAAAAAAAAA\\\\n; i=\$((i+1)); done; sleep 100'") ||
|
||||
exit 1
|
||||
sleep 0.2
|
||||
X1=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||
Y1=$($INNER display-message -p -t "$FLOAT" '#{pane_top}')
|
||||
H1=$($INNER display-message -p -t "$FLOAT" '#{pane_height}')
|
||||
local=$((RX - X1 - 1))
|
||||
if [ "$local" -ge 1 ] && [ "$local" -le 11 ] &&
|
||||
[ $((local % 2)) -eq 1 ]; then
|
||||
break
|
||||
fi
|
||||
startx=$((startx + 1))
|
||||
tries=$((tries + 1))
|
||||
done
|
||||
local=$((RX - X1 - 1))
|
||||
[ "$local" -ge 1 ] && [ "$local" -le 11 ] && [ $((local % 2)) -eq 1 ] ||
|
||||
fail "could not find bad-parity starting column"
|
||||
|
||||
$OUTER new-session -d -s outer -x 60 -y 12 'sleep 100' || exit 1
|
||||
$OUTER set-option -g status off || exit 1
|
||||
$OUTER set-option -g window-size manual || exit 1
|
||||
$OUTER set-option -g default-terminal screen-256color || exit 1
|
||||
$OUTER respawn-pane -k -t outer:0.0 \
|
||||
"$TEST_TMUX -Lpromptwide-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||
exit 1
|
||||
|
||||
wait_for_client
|
||||
wait_outer_has AAAAAAAAAAAAAAAAAAAAAA
|
||||
CLIENT=$($INNER list-clients -F '#{client_name}') || exit 1
|
||||
|
||||
$INNER select-pane -t "$FLOAT" || exit 1
|
||||
PROMPTTEXT=$(printf '\344\270\255' | perl -CSD -ne 'print $_ x 6')
|
||||
$INNER command-prompt -b -P -t "$CLIENT" -p "$PROMPTTEXT" \
|
||||
'display-message -- %1' || exit 1
|
||||
wait_outer_has "$PROMPTTEXT"
|
||||
|
||||
PROMPTROW=$((Y1 + H1))
|
||||
|
||||
# Trigger the damage: unblock the emitter so it fires the palette change in
|
||||
# the left tiled pane, which is occluded under (but geometrically overlaps)
|
||||
# the floating pane's prompt row.
|
||||
$INNER send-keys -t "$LEFT" Enter || exit 1
|
||||
|
||||
wait_prompt_row_intact
|
||||
|
||||
exit 0
|
||||
@@ -1,151 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Damage redraws must show each client's own pane status, even though the
|
||||
# cached status screen is shared between clients.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
INNER="$TEST_TMUX -Lstatuscc-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lstatuscc-outer-$$ -f/dev/null"
|
||||
CAPTURE=$DIR/capture
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
[ -s "$CAPTURE" ] && cat "$CAPTURE" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
wait_for_clients()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
count=$($INNER list-clients 2>/dev/null | wc -l)
|
||||
[ "$count" -eq 2 ] && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "two inner clients did not attach"
|
||||
}
|
||||
|
||||
wait_for_marker()
|
||||
{
|
||||
target=$1
|
||||
marker=$2
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t "$target" >"$CAPTURE" || exit 1
|
||||
grep -q "$marker" "$CAPTURE" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "client in $target did not receive $marker"
|
||||
}
|
||||
|
||||
cat >"$DIR/emitter.pl" <<'PERL'
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
$| = 1;
|
||||
for my $phase (1 .. 4) {
|
||||
while (!-e "$ENV{TRIGGER}-$phase") {
|
||||
select undef, undef, undef, 0.01;
|
||||
}
|
||||
# Change an unused palette entry and acknowledge it in the pane body.
|
||||
print "\e]4;200;rgb:11/22/0$phase\a\e[1;1HDAMAGE$phase";
|
||||
}
|
||||
sleep 100;
|
||||
PERL
|
||||
|
||||
$INNER new-session -d -s inner -x 60 -y 20 \
|
||||
"TRIGGER='$DIR/trigger' perl '$DIR/emitter.pl'" || exit 1
|
||||
$INNER set -g status off || exit 1
|
||||
$INNER set -g window-size manual || exit 1
|
||||
$INNER set -g automatic-rename off || exit 1
|
||||
$INNER set -g status-interval 0 || exit 1
|
||||
$INNER set -g pane-border-lines simple || exit 1
|
||||
$INNER set -g pane-border-status top || exit 1
|
||||
$INNER set -g pane-border-format 'CLIENT=<#{client_name}>' || exit 1
|
||||
FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x 35 -y 6 -X 5 -Y 5 \
|
||||
'sleep 100') || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 121 -y 20 'sleep 100' || exit 1
|
||||
$OUTER set -g status off || exit 1
|
||||
$OUTER set -g window-size manual || exit 1
|
||||
$OUTER set -g default-terminal screen || exit 1
|
||||
LEFT=$($OUTER display-message -p -t outer:0.0 '#{pane_id}') || exit 1
|
||||
RIGHT=$($OUTER split-window -h -PF '#{pane_id}' 'sleep 100') || exit 1
|
||||
for target in "$LEFT" "$RIGHT"; do
|
||||
$OUTER respawn-pane -k -t "$target" "$INNER attach-session -t inner" ||
|
||||
exit 1
|
||||
done
|
||||
wait_for_clients
|
||||
|
||||
NAME1=$($OUTER display-message -p -t "$LEFT" '#{pane_tty}') || exit 1
|
||||
NAME2=$($OUTER display-message -p -t "$RIGHT" '#{pane_tty}') || exit 1
|
||||
|
||||
# Disable periodic status updates above and trigger damage without a command
|
||||
# that also requests a status redraw. Each client must keep its own title.
|
||||
$INNER refresh-client -t "$NAME1" || exit 1
|
||||
$INNER refresh-client -t "$NAME2" || exit 1
|
||||
sleep 0.5
|
||||
for phase in 0 1 2; do
|
||||
if [ "$phase" -ne 0 ]; then
|
||||
: >"$DIR/trigger-$phase"
|
||||
wait_for_marker "$LEFT" "DAMAGE$phase"
|
||||
wait_for_marker "$RIGHT" "DAMAGE$phase"
|
||||
sleep 0.2
|
||||
fi
|
||||
for target in "$LEFT" "$RIGHT"; do
|
||||
if [ "$target" = "$LEFT" ]; then
|
||||
name=$NAME1
|
||||
other=$NAME2
|
||||
else
|
||||
name=$NAME2
|
||||
other=$NAME1
|
||||
fi
|
||||
$OUTER capture-pane -p -S 5 -E 10 -t "$target" \
|
||||
>"$CAPTURE" || exit 1
|
||||
grep -Fq "CLIENT=<$name>" "$CAPTURE" ||
|
||||
fail "phase $phase: missing $name's pane status"
|
||||
if grep -Fq "CLIENT=<$other>" "$CAPTURE"; then
|
||||
fail "phase $phase: $name received $other's pane status"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Leave just one client so a cache keyed only by client would remain stale.
|
||||
$OUTER respawn-pane -k -t "$RIGHT" 'sleep 100' || exit 1
|
||||
i=0
|
||||
while [ "$($INNER list-clients | wc -l)" -ne 1 ]; do
|
||||
[ "$i" -lt 50 ] || fail "second client did not detach"
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
$INNER set-environment -g TEST_STATUS_VALUE initial || exit 1
|
||||
$INNER set -g pane-border-format 'VALUE=#{TEST_STATUS_VALUE}' || exit 1
|
||||
$INNER refresh-client -t "$NAME1" || exit 1
|
||||
wait_for_marker "$LEFT" VALUE=initial
|
||||
for phase in 3 4; do
|
||||
# Changing the environment does not itself request a status redraw.
|
||||
$INNER set-environment -g TEST_STATUS_VALUE "phase$phase" || exit 1
|
||||
: >"$DIR/trigger-$phase"
|
||||
wait_for_marker "$LEFT" "DAMAGE$phase"
|
||||
wait_for_marker "$LEFT" "VALUE=phase$phase"
|
||||
done
|
||||
|
||||
exit 0
|
||||
@@ -1,102 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Resizing a floating pane must refresh session status formats which depend on
|
||||
# its geometry, not only the pane scene and borders.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
INNER="$TEST_TMUX -Lfloating-status-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lfloating-status-outer-$$ -f/dev/null"
|
||||
CAPTURE=$DIR/capture
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
[ -s "$CAPTURE" ] && cat "$CAPTURE" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
wait_for_client()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
CLIENT=$($INNER list-clients -F '#{client_name}' 2>/dev/null)
|
||||
[ -n "$CLIENT" ] && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "inner client did not attach"
|
||||
}
|
||||
|
||||
wait_outer_has_status()
|
||||
{
|
||||
marker=$1
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||
tail -1 "$CAPTURE" | grep -q "$marker" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "outer client status did not show $marker"
|
||||
}
|
||||
|
||||
mouse()
|
||||
{
|
||||
sequence=$(printf '\033[<%s;%s;%s%s' "$1" "$2" "$3" "$4")
|
||||
$OUTER send-keys -t outer:0.0 -l "$sequence" || exit 1
|
||||
sleep 0.1
|
||||
}
|
||||
|
||||
$INNER new-session -d -s inner -x 50 -y 12 'sleep 100' || exit 1
|
||||
FLOAT=$($INNER new-pane -PF '#{pane_id}' -x 10 -y 5 -X 5 -Y 3 \
|
||||
'sleep 100') || fail "could not create floating pane"
|
||||
$INNER set-option -g window-size manual || exit 1
|
||||
$INNER set-option -g mouse on || exit 1
|
||||
$INNER set-option -g status on || exit 1
|
||||
$INNER set-option -g status-position bottom || exit 1
|
||||
$INNER set-option -g status-left 'WIDTH=#{pane_width}' || exit 1
|
||||
$INNER set-option -g status-right '' || exit 1
|
||||
$INNER set-option -g status-interval 0 || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 50 -y 12 'sleep 100' || exit 1
|
||||
$OUTER set-option -g status off || exit 1
|
||||
$OUTER set-option -g window-size manual || exit 1
|
||||
$OUTER respawn-pane -k -t outer:0.0 \
|
||||
"$TEST_TMUX -Lfloating-status-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||
exit 1
|
||||
|
||||
wait_for_client
|
||||
OLD_WIDTH=$($INNER display-message -p -t "$FLOAT" '#{pane_width}')
|
||||
wait_outer_has_status "WIDTH=$OLD_WIDTH"
|
||||
|
||||
RIGHT=$($INNER display-message -p -t "$FLOAT" '#{pane_right}')
|
||||
TOP=$($INNER display-message -p -t "$FLOAT" '#{pane_top}')
|
||||
X=$((RIGHT + 2))
|
||||
Y=$((TOP + 2))
|
||||
|
||||
# Grab the right frame and enlarge the floating pane.
|
||||
mouse 0 "$X" "$Y" M
|
||||
mouse 32 "$((X + 1))" "$Y" M
|
||||
mouse 32 "$((X + 8))" "$Y" M
|
||||
mouse 0 "$((X + 8))" "$Y" m
|
||||
|
||||
NEW_WIDTH=$($INNER display-message -p -t "$FLOAT" '#{pane_width}')
|
||||
[ "$NEW_WIDTH" -ne "$OLD_WIDTH" ] || fail "floating pane was not resized"
|
||||
wait_outer_has_status "WIDTH=$NEW_WIDTH"
|
||||
|
||||
exit 0
|
||||
@@ -7,8 +7,6 @@ TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
trap '$TMUX kill-server 2>/dev/null' 0
|
||||
trap 'exit 1' 1 2 15
|
||||
|
||||
# test_format $format $expected_result
|
||||
test_format()
|
||||
|
||||
@@ -18,24 +18,6 @@ export TZ LANG LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
FIFO1="${TMPDIR:-/tmp}/fmt-l-$$-1"
|
||||
FIFO2="${TMPDIR:-/tmp}/fmt-l-$$-2"
|
||||
HOLD1=
|
||||
HOLD2=
|
||||
CC1=
|
||||
CC2=
|
||||
|
||||
cleanup()
|
||||
{
|
||||
for pid in $HOLD1 $HOLD2 $CC1 $CC2; do
|
||||
kill "$pid" 2>/dev/null
|
||||
wait "$pid" 2>/dev/null
|
||||
done
|
||||
$TMUX kill-server 2>/dev/null
|
||||
rm -f "$FIFO1" "$FIFO2"
|
||||
}
|
||||
trap cleanup 0
|
||||
trap 'exit 1' 1 2 15
|
||||
|
||||
ESC=$(printf '\033')
|
||||
|
||||
@@ -597,6 +579,8 @@ assert_alive "verbose loop expansion"
|
||||
|
||||
# L loops over attached clients. Attach two control-mode clients, each held
|
||||
# open by a background process keeping a FIFO's write end open.
|
||||
FIFO1="${TMPDIR:-/tmp}/fmt-l-$$-1"
|
||||
FIFO2="${TMPDIR:-/tmp}/fmt-l-$$-2"
|
||||
rm -f "$FIFO1" "$FIFO2"
|
||||
mkfifo "$FIFO1" "$FIFO2" || exit 1
|
||||
# Hold the write ends open so the control clients stay attached.
|
||||
@@ -621,9 +605,9 @@ test_format "#{L/nr:x}" "xx"
|
||||
test_format "#{L/r:x}" "xx"
|
||||
# Now detach one and confirm the count drops to one.
|
||||
kill $HOLD2 2>/dev/null
|
||||
wait $HOLD2 2>/dev/null
|
||||
HOLD2=
|
||||
sleep 1
|
||||
test_format "#{L:x}" "x"
|
||||
kill $HOLD1 $CC1 $CC2 2>/dev/null
|
||||
rm -f "$FIFO1" "$FIFO2"
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -7,8 +7,6 @@ TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
trap '$TMUX kill-server 2>/dev/null' 0
|
||||
trap 'exit 1' 1 2 15
|
||||
|
||||
# test_format $format $expected_result
|
||||
test_format()
|
||||
|
||||
@@ -76,14 +76,14 @@ $TMUX set-hook -g session-closed \
|
||||
fail "set-hook session-closed failed"
|
||||
|
||||
# The only pane of the only window of a session exits: pane-exited, then
|
||||
# session-closed, then window-unlinked, each seeing the dead object in the
|
||||
# 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|session-closed:doomed|window-unlinked:doomed:dwin"
|
||||
"|pane-exited:$pane|window-unlinked:doomed:dwin|session-closed:doomed"
|
||||
assert_unchanged @log \
|
||||
"|pane-exited:$pane|session-closed:doomed|window-unlinked:doomed:dwin"
|
||||
"|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.
|
||||
@@ -110,14 +110,14 @@ $TMUX set-hook -g pane-exited \
|
||||
'set -gF @log "#{@log}|pane-exited:#{hook_pane}"' ||
|
||||
fail "restore pane-exited hook failed"
|
||||
|
||||
# kill-window on the last window: session-closed then window-unlinked but
|
||||
# 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 '|session-closed:doomed2|window-unlinked:doomed2:dwin2'
|
||||
assert_unchanged @log '|session-closed:doomed2|window-unlinked:doomed2:dwin2'
|
||||
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
|
||||
|
||||
@@ -12,8 +12,7 @@ $TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
OUT=$(mktemp)
|
||||
trap 'rm -f "$TMP" "$OUT"; $TMUX kill-server 2>/dev/null' 0
|
||||
trap 'exit 1' 1 2 15
|
||||
trap "rm -f $TMP $OUT" 0 1 15
|
||||
|
||||
cat <<EOF >$TMP
|
||||
if 'true' 'wibble wobble'
|
||||
|
||||
@@ -4,9 +4,6 @@ TERM=screen
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
|
||||
# Keep panes alive until the next case or cleanup, without reading replies.
|
||||
INPUT_HOLD='while :; do sleep 3600; done'
|
||||
|
||||
TMP=$(mktemp)
|
||||
EXP=$(mktemp)
|
||||
trap 'rm -f "$TMP" "$EXP"; $TMUX kill-server 2>/dev/null' 0 1 15
|
||||
@@ -40,10 +37,10 @@ start_pane_hlimit()
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
sleep 0.1
|
||||
$TMUX new-session -d -x 1 -y 1 -s test-setup "$INPUT_HOLD" || exit 1
|
||||
$TMUX new-session -d -x 1 -y 1 -s test-setup "sleep 2" || exit 1
|
||||
$TMUX set-option -g history-limit "$hlimit" || exit 1
|
||||
$TMUX new-session -d -x "$sx" -y "$sy" -s "$name" \
|
||||
"printf '$seq'; $INPUT_HOLD" || exit 1
|
||||
"printf '$seq'; sleep 2" || exit 1
|
||||
$TMUX kill-session -t test-setup
|
||||
sleep 0.3
|
||||
}
|
||||
|
||||
+1
-13
@@ -22,19 +22,7 @@ assert_key () {
|
||||
clear-history -t$W \; \
|
||||
send-keys -t$W "$key" 'EOL' || exit 1
|
||||
|
||||
# cat echoes the keys back asynchronously, so wait for the EOL marker
|
||||
# to reach the pane instead of capturing straight away.
|
||||
i=0
|
||||
while [ $i -lt 50 ]; do
|
||||
screen=$($TMUX capturep -pt$W)
|
||||
case "$screen" in
|
||||
*EOL*) break ;;
|
||||
esac
|
||||
i=$((i + 1))
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
actual_code=$(printf '%s\n' "$screen" | \
|
||||
actual_code=$($TMUX capturep -pt$W | \
|
||||
head -1 | \
|
||||
sed -e 's/EOL.*$//')
|
||||
|
||||
|
||||
@@ -2,38 +2,6 @@
|
||||
|
||||
. ./input-common.inc
|
||||
|
||||
# Wait for the terminator and following text to reach the parser.
|
||||
check_discard()
|
||||
{
|
||||
name=$1
|
||||
printf 'OK\n' >"$EXP"
|
||||
i=0
|
||||
while [ "$i" -lt 300 ]; do
|
||||
capture_grid "$name" >"$TMP"
|
||||
cmp -s "$TMP" "$EXP" && return 0
|
||||
sleep 0.05
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "$name (timed out waiting for discard)"
|
||||
}
|
||||
|
||||
# The missing-terminator timer may expire before the string limit is reached
|
||||
# on a slow build. Clear any payload printed after the timeout, then check
|
||||
# that the string did not change the title and normal output has resumed.
|
||||
test_discard()
|
||||
{
|
||||
name=$1
|
||||
prefix=$2
|
||||
start_cmd "$name" 8 3 "$INPUT_HOLD"
|
||||
$TMUX select-pane -T discard-test || exit 1
|
||||
$TMUX respawn-pane -k \
|
||||
"perl -e 'print qq{$prefix}, q{x} x 1100000, qq{\e\\\\\e[H\e[2JOK}'; $INPUT_HOLD" || exit 1
|
||||
check_discard "$name"
|
||||
$TMUX display-message -p -t "$name:" '#{pane_title}' >"$TMP" || exit 1
|
||||
printf 'discard-test\n' >"$EXP"
|
||||
cmp "$TMP" "$EXP" || fail "$name title"
|
||||
}
|
||||
|
||||
start_cmd csi-param-discard 8 3 \
|
||||
"perl -e 'print qq{\e[}, q{1} x 80, qq{\030OK}'; sleep 2"
|
||||
check_capture csi-param-discard 'OK'
|
||||
@@ -42,8 +10,13 @@ start_cmd csi-interm-discard 8 3 \
|
||||
"perl -e 'print qq{\e[ \030OK}'; sleep 2"
|
||||
check_capture csi-interm-discard 'OK'
|
||||
|
||||
test_discard osc-discard '\e]2;'
|
||||
test_discard apc-discard '\e_'
|
||||
start_cmd osc-discard 8 3 \
|
||||
"perl -e 'print qq{\e]2;}, q{x} x 1100000, qq{\e\\\\OK}'; sleep 2"
|
||||
check_capture osc-discard 'OK'
|
||||
|
||||
start_cmd apc-discard 8 3 \
|
||||
"perl -e 'print qq{\e_}, q{x} x 1100000, qq{\e\\\\OK}'; sleep 2"
|
||||
check_capture apc-discard 'OK'
|
||||
|
||||
start_pane unknown-csi 8 3 '\033[?9999zOK'
|
||||
check_capture unknown-csi 'OK'
|
||||
|
||||
@@ -51,7 +51,7 @@ $TMUX kill-server 2>/dev/null
|
||||
sleep 0.1
|
||||
$TMUX new-session -d -x 5 -y 3 -s history \; \
|
||||
set-option -g history-limit 3 \; \
|
||||
respawn-pane -k "printf '01\n02\n03\n04\n05\n06'; $INPUT_HOLD" || exit 1
|
||||
respawn-pane -k "printf '01\n02\n03\n04\n05\n06'; sleep 2" || exit 1
|
||||
sleep 0.3
|
||||
$TMUX capture-pane -pN -t history: -S - -E - | normalize_capture >"$TMP"
|
||||
printf "%s\n" '01
|
||||
|
||||
@@ -377,7 +377,10 @@ must_equal 'Layout after select-pane back' "$(layout L:two)" "$SPLIT"
|
||||
|
||||
# With nothing zoomed the two layout formats agree.
|
||||
#
|
||||
# Zoomed layouts are checked below with floating panes.
|
||||
# The zoomed case is deliberately not covered here. While a pane is zoomed
|
||||
# #{window_layout} dumps the saved (unzoomed) layout and
|
||||
# #{window_visible_layout} the zoomed one, but that depends on how zooming
|
||||
# stashes the layout root rather than on anything in layout-custom.c.
|
||||
must_equal 'Visible layout' "$(visible_layout L:two)" "$SPLIT"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -701,42 +704,6 @@ must_contain 'Floating layout back z-index' "$floating" '"z":1'
|
||||
check_ok select-layout -t L:float "$floating"
|
||||
must_equal 'Floating layout after round trip' "$(raw_layout L:float)" "$floating"
|
||||
|
||||
# A dump taken while zoomed must retain the unzoomed floating z-indexes.
|
||||
# Check hidden floats, floats above zoom, and a mixture of the two.
|
||||
for flags in '' A mixed; do
|
||||
check_ok new-window -d -t L: -n zoom
|
||||
zt=$($TMUX display-message -p -t L:zoom '#{pane_id}')
|
||||
check_ok split-window -d -h -t "$zt"
|
||||
case "$flags" in
|
||||
A|mixed) first=-Ad ;;
|
||||
*) first=-d ;;
|
||||
esac
|
||||
case "$flags" in
|
||||
A) second=-Ad ;;
|
||||
*) second=-d ;;
|
||||
esac
|
||||
check_ok new-pane "$first" -t "$zt" -x 20 -y 6 ''
|
||||
check_ok new-pane "$second" -t "$zt" -x 30 -y 8 ''
|
||||
for pane in 0 2 3; do
|
||||
check_ok select-pane -t "L:zoom.$pane"
|
||||
before=$(raw_layout L:zoom)
|
||||
legacy=$(v1_layout L:zoom)
|
||||
check_ok resize-pane -Z -t "L:zoom.$pane"
|
||||
during=$(raw_layout L:zoom)
|
||||
must_equal 'Layout while zoomed' "$during" "$before"
|
||||
must_equal 'Legacy layout while zoomed' \
|
||||
"$(v1_layout L:zoom)" "$legacy"
|
||||
must_equal 'Zoom after dumping layout' \
|
||||
"$($TMUX display-message -p -t L:zoom '#{window_zoomed_flag}')" 1
|
||||
must_differ 'Visible layout while zoomed' \
|
||||
"$(visible_layout L:zoom)" "$(layout L:zoom)"
|
||||
check_ok select-layout -t L:zoom "$during"
|
||||
must_equal 'Round trip from zoomed layout' \
|
||||
"$(raw_layout L:zoom)" "$before"
|
||||
done
|
||||
check_ok kill-window -t L:zoom
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Floating panes and the legacy (v1) format.
|
||||
#
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Chooser menus are centred on the mouse using their displayed width. A
|
||||
# hidden title must not move a borderless menu away from the mouse.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -LtestA$$ -f/dev/null"
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server >/dev/null 2>&1
|
||||
$TMUX2 kill-server >/dev/null 2>&1
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
$TMUX new-session -d -s short -x 100 -y 30 'sleep 100' || exit 1
|
||||
$TMUX set -g mouse on || exit 1
|
||||
$TMUX set -g status off || exit 1
|
||||
$TMUX2 new-session -d -x 100 -y 30 "$TMUX attach" || exit 1
|
||||
sleep 1
|
||||
|
||||
for name in short aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; do
|
||||
$TMUX rename-session "$name" || exit 1
|
||||
for lines in none single padded; do
|
||||
$TMUX set -g menu-border-lines "$lines" || exit 1
|
||||
$TMUX choose-tree -s || exit 1
|
||||
sleep 1
|
||||
|
||||
# Right-button press at window (60, 0), away from either edge.
|
||||
seq=$(printf '\033[<2;61;1M')
|
||||
$TMUX2 send-keys -l "$seq" || exit 1
|
||||
sleep 1
|
||||
|
||||
row=2
|
||||
column=54
|
||||
if [ "$lines" = none ]; then
|
||||
row=1
|
||||
elif [ "$name" != short ]; then
|
||||
column=31
|
||||
fi
|
||||
text=$($TMUX2 capture-pane -p | awk -v row="$row" \
|
||||
-v column="$column" 'NR == row {
|
||||
# Keep byte offsets equal to columns with non-Unicode awk.
|
||||
gsub(/│/, "|")
|
||||
print substr($0, column, 7)
|
||||
}')
|
||||
[ "$text" = 'Select ' ] || \
|
||||
fail "$lines menu for $name: expected Select at ($column, $row), got '$text'"
|
||||
|
||||
# Close the menu, release the button, then leave the chooser.
|
||||
$TMUX2 send-keys Escape || exit 1
|
||||
seq=$(printf '\033[<2;61;1m')
|
||||
$TMUX2 send-keys -l "$seq" || exit 1
|
||||
$TMUX2 send-keys q || exit 1
|
||||
sleep 1
|
||||
done
|
||||
done
|
||||
|
||||
exit 0
|
||||
@@ -1,126 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Damage at a popup edge must redraw complete grid characters. Drawing only a
|
||||
# wide character's base or padding cell leaves a two-cell hole behind.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
INNER="$TEST_TMUX -Lpopup-wide-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lpopup-wide-outer-$$ -f/dev/null"
|
||||
EMITTER=$DIR/emitter.pl
|
||||
BASE=$DIR/base
|
||||
CAPTURE=$DIR/capture
|
||||
POPUP_PID=
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
[ -s "$CAPTURE" ] && cat "$CAPTURE" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
[ -n "$POPUP_PID" ] && kill "$POPUP_PID" 2>/dev/null
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
wait_for_client()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
CLIENT=$($INNER list-clients -F '#{client_name}' 2>/dev/null)
|
||||
[ -n "$CLIENT" ] && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "inner client did not attach"
|
||||
}
|
||||
|
||||
wait_outer_has()
|
||||
{
|
||||
marker=$1
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||
grep -q "$marker" "$CAPTURE" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "outer client did not show $marker"
|
||||
}
|
||||
|
||||
wait_old_rows_restored()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||
sed -n '3,5p' "$BASE" >"$DIR/want"
|
||||
sed -n '3,5p' "$CAPTURE" >"$DIR/got"
|
||||
cmp -s "$DIR/want" "$DIR/got" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "wide characters under the old popup edge were not restored"
|
||||
}
|
||||
|
||||
mouse()
|
||||
{
|
||||
sequence=$(printf '\033[<%s;%s;%s%s' "$1" "$2" "$3" "$4")
|
||||
$OUTER send-keys -t outer:0.0 -l "$sequence" || exit 1
|
||||
sleep 0.1
|
||||
}
|
||||
|
||||
cat >"$EMITTER" <<'PERL'
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
binmode STDOUT, ':encoding(UTF-8)';
|
||||
$| = 1;
|
||||
for my $row (1 .. 10) {
|
||||
print "\e[$row;1H", chr(0x754c) x 20;
|
||||
}
|
||||
sleep 100;
|
||||
PERL
|
||||
|
||||
$INNER new-session -d -s inner -x 40 -y 10 "perl '$EMITTER'" || exit 1
|
||||
$INNER set-option -g status off || exit 1
|
||||
$INNER set-option -g window-size manual || exit 1
|
||||
$INNER set-option -g mouse on || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 40 -y 10 'sleep 100' || exit 1
|
||||
$OUTER set-option -g status off || exit 1
|
||||
$OUTER set-option -g window-size manual || exit 1
|
||||
$OUTER set-option -g default-terminal screen-256color || exit 1
|
||||
$OUTER respawn-pane -k -t outer:0.0 \
|
||||
"$TEST_TMUX -Lpopup-wide-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||
exit 1
|
||||
|
||||
wait_for_client
|
||||
wait_outer_has '界界界'
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$BASE" || exit 1
|
||||
|
||||
$INNER display-popup -t "$CLIENT" -x 5 -y 5 -w 10 -h 3 -E \
|
||||
"sh -c 'printf POPUP; exec sleep 100'" &
|
||||
POPUP_PID=$!
|
||||
wait_outer_has POPUP
|
||||
|
||||
# The first motion starts the drag; the second moves the popup away from its
|
||||
# old rectangle. Its odd x coordinate bisects the underlying double-width
|
||||
# cells at both edges.
|
||||
mouse 0 10 3 M
|
||||
mouse 32 11 3 M
|
||||
mouse 32 28 7 M
|
||||
mouse 0 28 7 m
|
||||
wait_old_rows_restored
|
||||
|
||||
exit 0
|
||||
@@ -131,14 +131,6 @@ printf '%s\n' "$captured" | grep -Fq 'show-buffer' ||
|
||||
printf '%s\n' "$captured" | grep -Fq 'show-environment' ||
|
||||
fail "ambiguous completion list was incomplete"
|
||||
$OUTER send-keys Escape || exit 1
|
||||
# Wait until Escape has closed the prompt before sending another Meta key.
|
||||
# Otherwise the two can be parsed together as a single escape sequence.
|
||||
i=0
|
||||
while capture | grep -Fq '(word)'; do
|
||||
[ "$i" -lt 50 ] || fail "completion prompt did not close"
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
# Add entries to both history rings through real prompts.
|
||||
bind_prompt 'history-command'
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Many wrapped rows crossing a panned viewport produce disjoint damage.
|
||||
# Every row must survive the rectangle-count limit and subsequent merging.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
INNER="$TEST_TMUX -Laccumulate-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Laccumulate-outer-$$ -f/dev/null"
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
wait_marker()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$DIR/capture" || exit 1
|
||||
grep -q "$1" "$DIR/capture" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "client did not receive $1"
|
||||
}
|
||||
cat >"$DIR/emitter.pl" <<'PERL'
|
||||
use strict;
|
||||
use warnings;
|
||||
$| = 1;
|
||||
for my $row (1 .. 80) {
|
||||
printf "\e[%d;1HROW%02d-%s", $row, $row, 'abcdefghij' x 6;
|
||||
}
|
||||
print "\e[1;1H";
|
||||
while (!-e $ENV{TRIGGER}) {
|
||||
select undef, undef, undef, 0.01;
|
||||
}
|
||||
my $output = '';
|
||||
# Each wrapped second row needs a separate damage rectangle. Keep the
|
||||
# batch below the PTY read size so all 18 arrive in the same input pass.
|
||||
for my $region (0 .. 17) {
|
||||
my $top = 1 + $region * 4;
|
||||
$output .= "\e[$top;1H" . ('A' x 80) . ('B' x 24) .
|
||||
sprintf('DAMAGE%02d', $region) . ('B' x 48);
|
||||
}
|
||||
$output .= "\e[80;21HDONE";
|
||||
syswrite STDOUT, $output;
|
||||
while (!-e "$ENV{TRIGGER}-merge") {
|
||||
select undef, undef, undef, 0.01;
|
||||
}
|
||||
# Returning from the alternate screen requests a full-pane rectangle. A
|
||||
# later wrapped-row rectangle overlaps it and must not shrink that damage.
|
||||
$output = "\e[?1049h\e[40;21HALT-VISIBLE\e[?1049l";
|
||||
$output .= "\e[1;1H" . ('C' x 80) . ('D' x 24) . 'MERGE00' . ('D' x 49);
|
||||
$output .= "\e[80;21HMERGED";
|
||||
syswrite STDOUT, $output;
|
||||
sleep 100;
|
||||
PERL
|
||||
$INNER new-session -d -s inner -x 80 -y 80 \
|
||||
"TRIGGER='$DIR/trigger' perl '$DIR/emitter.pl'" || exit 1
|
||||
$INNER set -g status off || exit 1
|
||||
$INNER set -g window-size manual || exit 1
|
||||
$INNER set -g automatic-rename off || exit 1
|
||||
$INNER set -g status-interval 0 || exit 1
|
||||
$INNER set -as terminal-features ',screen:sync' || exit 1
|
||||
$INNER set -g pane-border-lines simple || exit 1
|
||||
$OUTER new-session -d -s outer -x 40 -y 80 'sleep 100' || exit 1
|
||||
$OUTER set -g status off || exit 1
|
||||
$OUTER set -g window-size manual || exit 1
|
||||
$OUTER set -g default-terminal screen || exit 1
|
||||
$OUTER respawn-pane -k -t outer:0.0 "$INNER attach -t inner" || exit 1
|
||||
wait_marker ROW80
|
||||
CLIENT=$($OUTER display -p -t outer:0.0 '#{pane_tty}') || exit 1
|
||||
$INNER refresh-client -t "$CLIENT" -R 20 || exit 1
|
||||
# Keep the emitter inactive so its writes use a synchronized frame. Otherwise
|
||||
# its own queued output can force a full redraw and hide lost rectangles.
|
||||
$INNER new-pane -x 6 -y 3 -X 65 -Y 74 'sleep 100' || exit 1
|
||||
sleep 0.2
|
||||
: >"$DIR/trigger"
|
||||
wait_marker DONE
|
||||
sleep 0.2
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$DIR/before" || exit 1
|
||||
region=0
|
||||
while [ "$region" -lt 18 ]; do
|
||||
marker=$(printf 'DAMAGE%02d' "$region")
|
||||
sed -n "$((2 + region * 4))p" "$DIR/before" | grep -q "$marker" ||
|
||||
fail "missing damage for region $region"
|
||||
region=$((region + 1))
|
||||
done
|
||||
$INNER refresh-client -t "$CLIENT" || exit 1
|
||||
sleep 0.2
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$DIR/after" || exit 1
|
||||
diff -u "$DIR/before" "$DIR/after" || fail "accumulated redraw differs from full redraw"
|
||||
: >"$DIR/trigger-merge"
|
||||
wait_marker MERGED
|
||||
sleep 0.2
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$DIR/before" || exit 1
|
||||
sed -n '2p' "$DIR/before" | grep -q MERGE00 || fail "merged damage lost the wrapped row"
|
||||
$INNER refresh-client -t "$CLIENT" || exit 1
|
||||
sleep 0.2
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$DIR/after" || exit 1
|
||||
diff -u "$DIR/before" "$DIR/after" || fail "merged redraw differs from full redraw"
|
||||
exit 0
|
||||
@@ -1,140 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# One terminal stops reading while another consumes floating-pane damage.
|
||||
# The slow client must catch up after the window's shared damage is cleared.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
INNER="$TEST_TMUX -Lblocked-inner-$$ -f/dev/null"
|
||||
FAST="$TEST_TMUX -Lblocked-fast-$$ -f/dev/null"
|
||||
SLOW="$TEST_TMUX -Lblocked-slow-$$ -f/dev/null"
|
||||
STOPPED=
|
||||
cleanup()
|
||||
{
|
||||
[ -n "$STOPPED" ] && kill -CONT "$STOPPED" 2>/dev/null
|
||||
$FAST kill-server 2>/dev/null
|
||||
$SLOW kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
wait_marker()
|
||||
{
|
||||
terminal=$1
|
||||
marker=$2
|
||||
i=0
|
||||
while [ "$i" -lt 100 ]; do
|
||||
$terminal capture-pane -p >"$DIR/capture" || exit 1
|
||||
grep -q "$marker" "$DIR/capture" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "client did not receive $marker"
|
||||
}
|
||||
mouse()
|
||||
{
|
||||
sequence=$(printf '\033[<%s;%s;%s%s' "$1" "$2" "$3" "$4")
|
||||
$FAST send-keys -l "$sequence" || exit 1
|
||||
sleep 0.2
|
||||
}
|
||||
cat >"$DIR/emitter.pl" <<'PERL'
|
||||
use strict;
|
||||
use warnings;
|
||||
$| = 1;
|
||||
print 'READY';
|
||||
while (!-e "$ENV{TRIGGER}-flood") {
|
||||
select undef, undef, undef, 0.01;
|
||||
}
|
||||
my $frame = 0;
|
||||
while (!-e "$ENV{TRIGGER}-stop") {
|
||||
my $output = '';
|
||||
for my $row (1 .. 20) {
|
||||
$output .= "\e[$row;1H" . join('', map { chr(33 + ($_ + $frame) % 80) } 0 .. 77);
|
||||
}
|
||||
print $output;
|
||||
$frame++;
|
||||
select undef, undef, undef, 0.01;
|
||||
}
|
||||
for my $row (1 .. 20) {
|
||||
printf "\e[%d;1HFINAL%02d-%s", $row, $row, '0123456789' x 7;
|
||||
}
|
||||
print "\e[20;1HDONE";
|
||||
sleep 100;
|
||||
PERL
|
||||
$INNER new-session -d -s inner -x 160 -y 80 \
|
||||
"TRIGGER='$DIR/trigger' perl '$DIR/emitter.pl'" || exit 1
|
||||
$INNER set -g status off || exit 1
|
||||
$INNER set -g window-size manual || exit 1
|
||||
$INNER set -g automatic-rename off || exit 1
|
||||
$INNER set -g status-interval 0 || exit 1
|
||||
$INNER set -g mouse on || exit 1
|
||||
$INNER set -g pane-border-lines simple || exit 1
|
||||
FLOAT=$($INNER new-pane -PF '#{pane_id}' -x 16 -y 5 -X 5 -Y 5 \
|
||||
'printf FLOAT; exec sleep 100') || exit 1
|
||||
for terminal in "$FAST" "$SLOW"; do
|
||||
$terminal new-session -d -x 160 -y 80 'sleep 100' || exit 1
|
||||
$terminal set -g status off || exit 1
|
||||
$terminal set -g window-size manual || exit 1
|
||||
$terminal set -g default-terminal screen || exit 1
|
||||
$terminal respawn-pane -k "$INNER attach -t inner" || exit 1
|
||||
wait_marker "$terminal" READY
|
||||
done
|
||||
FASTCLIENT=$($FAST display -p '#{pane_tty}') || exit 1
|
||||
SLOWCLIENT=$($SLOW display -p '#{pane_tty}') || exit 1
|
||||
SLOWPID=$($SLOW display -p '#{pid}') || exit 1
|
||||
before=$($INNER display -p -c "$FASTCLIENT" '#{client_written}') || exit 1
|
||||
slowbefore=$($INNER display -p -c "$SLOWCLIENT" '#{client_written}') || exit 1
|
||||
# The large terminal keeps this backlog below the discard threshold, so
|
||||
# automatic recovery from discarded output cannot mask lost damage.
|
||||
# Stop only our outer server, leaving its inner client attached to a PTY
|
||||
# whose master is no longer read. This creates real terminal backpressure.
|
||||
STOPPED=$SLOWPID
|
||||
kill -STOP "$SLOWPID" || exit 1
|
||||
: >"$DIR/trigger-flood"
|
||||
i=0
|
||||
while :; do
|
||||
written=$($INNER display -p -c "$SLOWCLIENT" '#{client_written}') || exit 1
|
||||
fastwritten=$($INNER display -p -c "$FASTCLIENT" '#{client_written}') || exit 1
|
||||
# More than a PTY can buffer has been queued for the stopped terminal,
|
||||
# while the other terminal is still receiving the same output.
|
||||
[ "$written" -gt "$((slowbefore + 65536))" ] &&
|
||||
[ "$fastwritten" -gt "$((before + 65536))" ] && break
|
||||
[ "$i" -lt 100 ] || fail "slow client did not become blocked"
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
: >"$DIR/trigger-stop"
|
||||
wait_marker "$FAST" DONE
|
||||
[ "$($INNER display -p -c "$FASTCLIENT" '#{client_discarded}')" -eq 0 ] ||
|
||||
fail "fast client also became blocked"
|
||||
mouse 0 12 6 M
|
||||
mouse 32 42 6 M
|
||||
mouse 0 42 6 m
|
||||
[ "$($INNER display -p -t "$FLOAT" '#{pane_left}')" -eq 36 ] || fail "pane did not move"
|
||||
$FAST capture-pane -p >"$DIR/fast-before" || exit 1
|
||||
# Ensure the fast client already restored the vacated frame.
|
||||
sed -n '6p' "$DIR/fast-before" | grep -q '^FINAL06-0123456789' ||
|
||||
fail "fast client did not restore old footprint"
|
||||
kill -CONT "$SLOWPID" || exit 1
|
||||
STOPPED=
|
||||
wait_marker "$SLOW" DONE
|
||||
# Allow queued terminal output and deferred redraws to drain.
|
||||
sleep 0.5
|
||||
[ "$($INNER display -p -c "$SLOWCLIENT" '#{client_discarded}')" -eq 0 ] ||
|
||||
fail "discard recovery could mask lost deferred damage"
|
||||
$SLOW capture-pane -p >"$DIR/slow-before" || exit 1
|
||||
diff -u "$DIR/fast-before" "$DIR/slow-before" || fail "slow client did not catch up"
|
||||
$INNER refresh-client -t "$FASTCLIENT" || exit 1
|
||||
sleep 0.2
|
||||
$FAST capture-pane -p >"$DIR/after" || exit 1
|
||||
diff -u "$DIR/fast-before" "$DIR/after" || fail "damage redraw differed from full redraw"
|
||||
exit 0
|
||||
@@ -1,112 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Check a redraw callback which has no accompanying client redraw flags. A
|
||||
# wrapped row crossing a panned viewport cannot use the direct tty path.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
INNER="$TEST_TMUX -Ldamage-only-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Ldamage-only-outer-$$ -f/dev/null"
|
||||
EMITTER=$DIR/emitter.pl
|
||||
TRIGGER=$DIR/trigger
|
||||
CAPTURE=$DIR/capture
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
[ -s "$CAPTURE" ] && cat "$CAPTURE" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
wait_for_client()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
CLIENT=$($INNER list-clients -F '#{client_name}' 2>/dev/null)
|
||||
[ -n "$CLIENT" ] && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "inner client did not attach"
|
||||
}
|
||||
|
||||
wait_outer_has()
|
||||
{
|
||||
marker=$1
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||
grep -q "$marker" "$CAPTURE" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "outer client did not show $marker"
|
||||
}
|
||||
|
||||
wait_inner_has()
|
||||
{
|
||||
marker=$1
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$INNER capture-pane -p -t inner:0.0 2>/dev/null |
|
||||
grep -q "$marker" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "inner pane did not contain $marker"
|
||||
}
|
||||
|
||||
cat >"$EMITTER" <<'PERL'
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
$| = 1;
|
||||
for my $row (1 .. 12) {
|
||||
print "\e[$row;1H", 'o' x 79;
|
||||
}
|
||||
print "\e[1;1H";
|
||||
while (!-e $ENV{TRIGGER}) {
|
||||
select undef, undef, undef, 0.01;
|
||||
}
|
||||
|
||||
my $second = ('B' x 24) . 'DAMAGE-ONLY' . ('B' x 45);
|
||||
print "\e[5;1H", ('A' x 80), $second;
|
||||
sleep 100;
|
||||
PERL
|
||||
|
||||
$INNER new-session -d -s inner -x 80 -y 12 \
|
||||
"TRIGGER='$TRIGGER' perl '$EMITTER'" || exit 1
|
||||
$INNER set-option -g status off || exit 1
|
||||
$INNER set-option -g window-size manual || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 40 -y 12 'sleep 100' || exit 1
|
||||
$OUTER set-option -g status off || exit 1
|
||||
$OUTER set-option -g window-size manual || exit 1
|
||||
$OUTER set-option -g default-terminal screen || exit 1
|
||||
$OUTER respawn-pane -k -t outer:0.0 \
|
||||
"$TEST_TMUX -Ldamage-only-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||
exit 1
|
||||
|
||||
wait_for_client
|
||||
$INNER refresh-client -t "$CLIENT" -R 20 || exit 1
|
||||
wait_outer_has oooooooooo
|
||||
|
||||
: >"$TRIGGER"
|
||||
wait_inner_has DAMAGE-ONLY
|
||||
wait_outer_has DAMAGE-ONLY
|
||||
|
||||
exit 0
|
||||
@@ -1,112 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Moving and interactively resizing floating panes must restore both clients,
|
||||
# including a smaller client panned horizontally and vertically.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
INNER="$TEST_TMUX -Lviewports-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lviewports-outer-$$ -f/dev/null"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
mouse()
|
||||
{
|
||||
sequence=$(printf '\033[<%s;%s;%s%s' "$1" "$2" "$3" "$4")
|
||||
$OUTER send-keys -t wide:0.0 -l "$sequence" || exit 1
|
||||
sleep 0.2
|
||||
}
|
||||
assert_scene()
|
||||
{
|
||||
# Compare what each terminal actually received with a fresh full redraw.
|
||||
# Capture both first: refreshing one client must not repair the other.
|
||||
for target in wide small; do
|
||||
$OUTER capture-pane -p -t "$target:0.0" >"$DIR/$target-before" || exit 1
|
||||
done
|
||||
$INNER refresh-client -t "$WIDE" || exit 1
|
||||
$INNER refresh-client -t "$SMALL" || exit 1
|
||||
sleep 0.2
|
||||
for target in wide small; do
|
||||
$OUTER capture-pane -p -t "$target:0.0" >"$DIR/$target-after" || exit 1
|
||||
diff -u "$DIR/$target-before" "$DIR/$target-after" ||
|
||||
fail "$1: $target client differed from a full redraw"
|
||||
done
|
||||
}
|
||||
|
||||
cat >"$DIR/background.pl" <<'PERL'
|
||||
$| = 1;
|
||||
for my $row (1 .. 24) {
|
||||
printf "\e[%d;1HROW%02d-", $row, $row;
|
||||
print '0123456789' x 7;
|
||||
}
|
||||
sleep 100;
|
||||
PERL
|
||||
$INNER new-session -d -s inner -x 80 -y 24 "perl '$DIR/background.pl'" || exit 1
|
||||
$INNER set -g status off || exit 1
|
||||
$INNER set -g window-size manual || exit 1
|
||||
$INNER set -g automatic-rename off || exit 1
|
||||
$INNER set -g status-interval 0 || exit 1
|
||||
$INNER set -g mouse on || exit 1
|
||||
$INNER set -g default-command 'sleep 100' || exit 1
|
||||
$INNER set -g pane-border-lines simple || exit 1
|
||||
$OUTER new-session -d -s wide -x 80 -y 24 'sleep 100' || exit 1
|
||||
$OUTER set -g status off || exit 1
|
||||
$OUTER set -g window-size manual || exit 1
|
||||
$OUTER set -g default-terminal screen || exit 1
|
||||
$OUTER new-session -d -s small -x 40 -y 12 'sleep 100' || exit 1
|
||||
for target in wide small; do
|
||||
$OUTER respawn-pane -k -t "$target:0.0" "$INNER attach -t inner" || exit 1
|
||||
done
|
||||
i=0
|
||||
while [ "$($INNER list-clients | wc -l)" -ne 2 ]; do
|
||||
[ "$i" -lt 50 ] || fail "two clients did not attach"
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
WIDE=$($OUTER display -p -t wide:0.0 '#{pane_tty}') || exit 1
|
||||
SMALL=$($OUTER display -p -t small:0.0 '#{pane_tty}') || exit 1
|
||||
$INNER refresh-client -t "$SMALL" -R 20 || exit 1
|
||||
$INNER refresh-client -t "$SMALL" -D 6 || exit 1
|
||||
FLOAT=$($INNER new-pane -d -PF '#{pane_id}' -x 16 -y 6 -X 25 -Y 9) || exit 1
|
||||
sleep 0.3
|
||||
assert_scene initial
|
||||
# Confirm that the two clients really have different viewports.
|
||||
[ "$(head -1 "$DIR/wide-before" | cut -c1-6)" = ROW01- ] || fail "wrong wide viewport"
|
||||
[ "$(head -1 "$DIR/small-before" | cut -c1-6)" != ROW01- ] || fail "small client was not panned"
|
||||
|
||||
# Meta-drag the body using move-pane -M, including a partially clipped position.
|
||||
mouse 8 30 12 M
|
||||
mouse 40 48 15 M
|
||||
[ "$($INNER display -p -t "$FLOAT" '#{pane_left}')" -eq 44 ] || fail "Meta-drag did not move pane"
|
||||
assert_scene move-right
|
||||
mouse 40 28 10 M
|
||||
assert_scene move-back
|
||||
mouse 8 28 10 m
|
||||
|
||||
# Ctrl-drag creates a new floating pane, then changes its size while held.
|
||||
mouse 16 52 14 M
|
||||
mouse 48 75 23 M
|
||||
NEW=$($INNER display -p '#{pane_id}') || exit 1
|
||||
[ "$NEW" != "$FLOAT" ] || fail "Ctrl-drag did not create a pane"
|
||||
[ "$($INNER display -p -t "$NEW" '#{pane_floating_flag}')" -eq 1 ] || fail "new pane is not floating"
|
||||
assert_scene create
|
||||
mouse 48 64 19 M
|
||||
[ "$($INNER display -p -t "$NEW" '#{pane_width}')" -eq 11 ] || fail "Ctrl-drag did not shrink pane"
|
||||
assert_scene shrink
|
||||
mouse 16 64 19 m
|
||||
assert_scene release
|
||||
exit 0
|
||||
@@ -1,186 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Redraw a moved floating pane on both attached clients viewing the same
|
||||
# window. Window redraw work must not be consumed by only one client.
|
||||
#
|
||||
# Uses ASCII pane borders (rather than the default UTF-8 box-drawing) because
|
||||
# this test nests a real tmux client inside another tmux's pane to get a
|
||||
# genuine terminal to capture from; that nested-tmux relay has been observed
|
||||
# to mis-render a cell that previously held a multi-byte UTF-8 border
|
||||
# character being overwritten later by plain content, on the outer instance's
|
||||
# own interpretation, independent of anything the inner tmux sends. That is a
|
||||
# nested-test-harness artifact, not a real tmux bug - confirmed by replaying
|
||||
# the exact same drag sequence against a real terminal (xterm), where it
|
||||
# never reproduces. ASCII borders avoid the artifact entirely.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
INNER="$TEST_TMUX -Lredraw-multi-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lredraw-multi-outer-$$ -f/dev/null"
|
||||
CAPTURE=$DIR/capture
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
[ -s "$CAPTURE" ] && cat "$CAPTURE" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
wait_for_clients()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
count=$($INNER list-clients 2>/dev/null | wc -l)
|
||||
[ "$count" -eq 2 ] && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "two inner clients did not attach"
|
||||
}
|
||||
|
||||
wait_outer_has()
|
||||
{
|
||||
target=$1
|
||||
marker=$2
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t "$target" >"$CAPTURE" 2>/dev/null || true
|
||||
grep -q "$marker" "$CAPTURE" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "outer pane $target did not show $marker"
|
||||
}
|
||||
|
||||
wait_float_left()
|
||||
{
|
||||
comparison=$1
|
||||
limit=$2
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
left=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||
if [ "$comparison" = gt ] && [ "$left" -gt "$limit" ]; then
|
||||
return 0
|
||||
fi
|
||||
if [ "$comparison" = lt ] && [ "$left" -lt "$limit" ]; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "floating pane did not move"
|
||||
}
|
||||
|
||||
mouse()
|
||||
{
|
||||
sequence=$(printf '\033[<%s;%s;%s%s' "$2" "$3" "$4" "$5")
|
||||
$OUTER send-keys -t "$1" -l "$sequence" || exit 1
|
||||
sleep 0.1
|
||||
}
|
||||
|
||||
drag_float()
|
||||
{
|
||||
target=$1
|
||||
startcol=$2
|
||||
startrow=$3
|
||||
endcol=$4
|
||||
mouse "$target" 0 "$startcol" "$startrow" M
|
||||
mouse "$target" 32 "$endcol" "$startrow" M
|
||||
mouse "$target" 0 "$endcol" "$startrow" m
|
||||
}
|
||||
|
||||
assert_scene()
|
||||
{
|
||||
target=$1
|
||||
base=$2
|
||||
firstcol=$3
|
||||
lastcol=$4
|
||||
|
||||
$OUTER capture-pane -p -t "$target" >"$CAPTURE" || exit 1
|
||||
|
||||
# With ASCII (simple) borders every corner and junction is the same
|
||||
# '+', so one rectangular floating pane always draws exactly 4 of
|
||||
# them; more means a stale frame was left behind somewhere.
|
||||
corners=$(grep -o '+' "$CAPTURE" | wc -l)
|
||||
[ "$corners" -eq 4 ] ||
|
||||
fail "outer pane $target had $corners floating frames"
|
||||
|
||||
sed -n '6,11p' "$base" | cut -c"$firstcol-$lastcol" >"$DIR/want"
|
||||
sed -n '6,11p' "$CAPTURE" | cut -c"$firstcol-$lastcol" >"$DIR/got"
|
||||
cmp -s "$DIR/want" "$DIR/got" ||
|
||||
fail "outer pane $target did not restore the old floating area"
|
||||
}
|
||||
|
||||
C="sh -c 'i=0; while [ \$i -lt 20 ]; do printf \"\\033[%d;1HBG-ROW-%02d-abcdefghijklmnopqrstuvwxyz0123456789\" \$((i + 1)) \$i; i=\$((i + 1)); done; exec sleep 100'"
|
||||
|
||||
$INNER new-session -d -s inner -x 60 -y 20 "$C" || exit 1
|
||||
$INNER set-option -g status off || exit 1
|
||||
$INNER set-option -g window-size manual || exit 1
|
||||
$INNER set-option -g mouse on || exit 1
|
||||
$INNER set-option -g default-command 'sleep 100' || exit 1
|
||||
$INNER set-option -g pane-border-lines simple || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 121 -y 20 'sleep 100' || exit 1
|
||||
$OUTER set-option -g status off || exit 1
|
||||
$OUTER set-option -g window-size manual || exit 1
|
||||
$OUTER set-option -g default-terminal screen || exit 1
|
||||
$OUTER split-window -h -t outer:0.0 'sleep 100' || exit 1
|
||||
|
||||
PANES=$($OUTER list-panes -t outer:0 -F '#{pane_id} #{pane_left}')
|
||||
LEFT=$(echo "$PANES" | sort -k2 -n | head -1 | cut -d' ' -f1)
|
||||
RIGHT=$(echo "$PANES" | sort -k2 -n | tail -1 | cut -d' ' -f1)
|
||||
[ -n "$LEFT" ] && [ -n "$RIGHT" ] || fail "could not find outer panes"
|
||||
|
||||
$OUTER respawn-pane -k -t "$LEFT" \
|
||||
"$TEST_TMUX -Lredraw-multi-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||
exit 1
|
||||
$OUTER respawn-pane -k -t "$RIGHT" \
|
||||
"$TEST_TMUX -Lredraw-multi-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||
exit 1
|
||||
|
||||
wait_for_clients
|
||||
wait_outer_has "$LEFT" BG-ROW-19
|
||||
wait_outer_has "$RIGHT" BG-ROW-19
|
||||
$OUTER capture-pane -p -t "$LEFT" >"$DIR/base-left" || exit 1
|
||||
$OUTER capture-pane -p -t "$RIGHT" >"$DIR/base-right" || exit 1
|
||||
|
||||
FLOAT=$($INNER new-pane -dPF '#{pane_id}' -x 16 -y 5 -X 5 -Y 5) ||
|
||||
fail "could not create floating pane"
|
||||
wait_outer_has "$LEFT" '+'
|
||||
wait_outer_has "$RIGHT" '+'
|
||||
|
||||
FTOP=$($INNER display-message -p -t "$FLOAT" '#{pane_top}')
|
||||
FLEFT=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||
FWIDTH=$($INNER display-message -p -t "$FLOAT" '#{pane_width}')
|
||||
GRABCOL=$((FLEFT + FWIDTH / 2 + 1))
|
||||
|
||||
# Move right through one client and require both clients to restore the old
|
||||
# left-hand footprint.
|
||||
drag_float "$LEFT" "$GRABCOL" "$FTOP" $((GRABCOL + 30))
|
||||
wait_float_left gt 30
|
||||
assert_scene "$LEFT" "$DIR/base-left" 1 20
|
||||
assert_scene "$RIGHT" "$DIR/base-right" 1 20
|
||||
|
||||
# Move back through the other client and check the old right-hand footprint.
|
||||
FLEFT=$($INNER display-message -p -t "$FLOAT" '#{pane_left}')
|
||||
GRABCOL=$((FLEFT + FWIDTH / 2 + 1))
|
||||
drag_float "$RIGHT" "$GRABCOL" "$FTOP" $((GRABCOL - 30))
|
||||
wait_float_left lt 10
|
||||
assert_scene "$LEFT" "$DIR/base-left" 35 60
|
||||
assert_scene "$RIGHT" "$DIR/base-right" 35 60
|
||||
|
||||
exit 0
|
||||
@@ -1,208 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Check that full and region screen-write fallbacks update an attached client,
|
||||
# not only tmux's internal pane grid.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
EMITTER=$DIR/emitter.pl
|
||||
CAPTURE=$DIR/capture
|
||||
INNER=
|
||||
OUTER=
|
||||
N=0
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
[ -s "$CAPTURE" ] && cat "$CAPTURE" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
[ -n "$OUTER" ] && $OUTER kill-server 2>/dev/null
|
||||
[ -n "$INNER" ] && $INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
wait_outer_has()
|
||||
{
|
||||
marker=$1
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||
grep -q "$marker" "$CAPTURE" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "outer client did not show $marker"
|
||||
}
|
||||
|
||||
wait_outer_lacks()
|
||||
{
|
||||
marker=$1
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||
grep -q "$marker" "$CAPTURE" || return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "outer client still showed $marker"
|
||||
}
|
||||
|
||||
wait_inner_has()
|
||||
{
|
||||
marker=$1
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$INNER capture-pane -p -t inner:0.0 2>/dev/null |
|
||||
grep -q "$marker" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "inner pane did not contain $marker"
|
||||
}
|
||||
|
||||
wait_inner_lacks()
|
||||
{
|
||||
marker=$1
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$INNER capture-pane -p -t inner:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||
grep -q "$marker" "$CAPTURE" || return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "inner pane still contained $marker"
|
||||
}
|
||||
|
||||
setup()
|
||||
{
|
||||
mode=$1
|
||||
[ -n "$OUTER" ] && $OUTER kill-server 2>/dev/null
|
||||
[ -n "$INNER" ] && $INNER kill-server 2>/dev/null
|
||||
N=$((N + 1))
|
||||
INNER="$TEST_TMUX -Lredraw-write-inner-$$-$N -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lredraw-write-outer-$$-$N -f/dev/null"
|
||||
|
||||
$INNER new-session -d -s inner -x 40 -y 12 \
|
||||
"MODE=$mode READY='$DIR/ready-$N' TRIGGER='$DIR/trigger-$N' perl '$EMITTER'" ||
|
||||
exit 1
|
||||
$INNER set-option -g status off || exit 1
|
||||
$INNER set-option -g window-size manual || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 40 -y 12 'sleep 100' || exit 1
|
||||
$OUTER set-option -g status off || exit 1
|
||||
$OUTER set-option -g window-size manual || exit 1
|
||||
$OUTER set-option -g default-terminal screen || exit 1
|
||||
$OUTER respawn-pane -k -t outer:0.0 \
|
||||
"$TEST_TMUX -Lredraw-write-inner-$$-$N -f/dev/null attach-session -t inner" ||
|
||||
exit 1
|
||||
}
|
||||
|
||||
trigger()
|
||||
{
|
||||
: >"$DIR/trigger-$N-${1:-1}"
|
||||
}
|
||||
|
||||
cat >"$EMITTER" <<'PERL'
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
$| = 1;
|
||||
my $mode = $ENV{MODE};
|
||||
my $ready = $ENV{READY};
|
||||
my $trigger = $ENV{TRIGGER};
|
||||
|
||||
sub fill_screen {
|
||||
my ($prefix) = @_;
|
||||
print "\e[2J\e[H";
|
||||
for my $row (0 .. 11) {
|
||||
printf "\e[%d;1H%s-ROW-%02d", $row + 1, $prefix, $row;
|
||||
}
|
||||
}
|
||||
|
||||
if ($mode eq 'ris') {
|
||||
fill_screen('RIS');
|
||||
} elsif ($mode eq 'alternate') {
|
||||
fill_screen('BASE');
|
||||
} elsif ($mode eq 'scroll') {
|
||||
fill_screen('SCROLL');
|
||||
} else {
|
||||
die "unknown mode $mode\n";
|
||||
}
|
||||
|
||||
open my $fh, '>', $ready or die "$ready: $!\n";
|
||||
close $fh;
|
||||
while (!-e "$trigger-1") {
|
||||
select undef, undef, undef, 0.01;
|
||||
}
|
||||
|
||||
if ($mode eq 'ris') {
|
||||
print "\ec";
|
||||
} elsif ($mode eq 'alternate') {
|
||||
print "\e[?1049h";
|
||||
fill_screen('ALT');
|
||||
while (!-e "$trigger-2") {
|
||||
select undef, undef, undef, 0.01;
|
||||
}
|
||||
print "\e[?1049l";
|
||||
} else {
|
||||
print "\e[12;1H\r\nSCROLL-NEW";
|
||||
}
|
||||
sleep 100;
|
||||
PERL
|
||||
|
||||
# RIS clears the complete screen. The source pane and attached client must both
|
||||
# lose every old row.
|
||||
setup ris
|
||||
wait_outer_has RIS-ROW-11
|
||||
trigger
|
||||
wait_inner_lacks RIS-ROW
|
||||
wait_outer_lacks RIS-ROW
|
||||
|
||||
# Leaving the alternate screen restores every row of the base screen.
|
||||
setup alternate
|
||||
wait_outer_has BASE-ROW-11
|
||||
trigger
|
||||
wait_inner_has ALT-ROW-11
|
||||
wait_outer_has ALT-ROW-11
|
||||
wait_outer_lacks BASE-ROW
|
||||
trigger 2
|
||||
wait_inner_has BASE-ROW-11
|
||||
wait_outer_has BASE-ROW-11
|
||||
wait_outer_lacks ALT-ROW
|
||||
|
||||
# Scrolling a pane which is narrower than the terminal redraws its complete
|
||||
# region. Check the physical client row by row after the source grid shifts.
|
||||
setup scroll
|
||||
$INNER split-window -h -t inner:0 'sleep 100' || exit 1
|
||||
wait_outer_has SCROLL-ROW-11
|
||||
trigger
|
||||
wait_inner_has SCROLL-NEW
|
||||
wait_outer_has SCROLL-NEW
|
||||
row=1
|
||||
while [ "$row" -le 11 ]; do
|
||||
expected=$(printf 'SCROLL-ROW-%02d' "$row")
|
||||
actual=$(sed -n "${row}p" "$CAPTURE")
|
||||
case "$actual" in
|
||||
"$expected"*) ;;
|
||||
*) fail "outer row $row was not redrawn as $expected" ;;
|
||||
esac
|
||||
row=$((row + 1))
|
||||
done
|
||||
actual=$(sed -n '12p' "$CAPTURE")
|
||||
case "$actual" in
|
||||
SCROLL-NEW*) ;;
|
||||
*) fail "outer bottom row was not redrawn as SCROLL-NEW" ;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
@@ -15,7 +15,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest$$"
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
DIR=$(mktemp -d)
|
||||
|
||||
@@ -22,26 +22,14 @@ check_directory()
|
||||
fi
|
||||
}
|
||||
|
||||
# The start path is available immediately, before the child is scheduled.
|
||||
# Use it to test the format context in the same command queue as creation.
|
||||
# Expand the current pane's directory in the same command queue as creation.
|
||||
$TMUX new-session -d -s test -c "$TMP/first" 'sleep 60' \; \
|
||||
run-shell -c '#{pane_start_path}' "pwd >'$TMP/out'" || exit 1
|
||||
run-shell -c '#{pane_current_path}' "pwd >'$TMP/out'" || exit 1
|
||||
check_directory "$TMP/first"
|
||||
|
||||
# An explicit target must supply the format context, including with a delay.
|
||||
pane=$($TMUX new-window -d -P -F '#{pane_id}' -t test \
|
||||
-c "$TMP/second dir" 'sleep 60') || exit 1
|
||||
# The current path depends on the operating system finding the child process.
|
||||
i=0
|
||||
while [ "$($TMUX display-message -p -t "$pane" '#{pane_current_path}')" != \
|
||||
"$TMP/second dir" ]; do
|
||||
if [ "$i" -ge 100 ]; then
|
||||
echo "Timed out waiting for pane current directory"
|
||||
exit 1
|
||||
fi
|
||||
sleep 0.05
|
||||
i=$((i + 1))
|
||||
done
|
||||
$TMUX run-shell -t "$pane" -d 0.1 -c '#{pane_current_path}' \
|
||||
"pwd >'$TMP/out'" || exit 1
|
||||
check_directory "$TMP/second dir"
|
||||
|
||||
@@ -106,28 +106,9 @@ compare menu-over-split
|
||||
|
||||
# Menu with no border lines.
|
||||
setup 40 14
|
||||
$TMUX2 display-menu -T "This title must not reserve width" -C 1 \
|
||||
-b none -x6 -y8 \
|
||||
"Alpha item" a "" \
|
||||
"Beta item" b "" \
|
||||
"" "" "" \
|
||||
"Gamma item" g "" || exit 1
|
||||
sleep 1
|
||||
menu -b none -x6 -y8
|
||||
compare menu-noborder
|
||||
|
||||
# Menu border style follows the explicitly targeted window, not the current
|
||||
# window when a different pane is selected.
|
||||
setup 40 14
|
||||
$TMUX2 set menu-border-lines none || exit 1
|
||||
$TMUX2 neww || exit 1
|
||||
$TMUX2 display-menu -t%0 -T "Menu" -C 1 \
|
||||
"Alpha item" a "" \
|
||||
"Beta item" b "" \
|
||||
"" "" "" \
|
||||
"Gamma item" g "" || exit 1
|
||||
$TMUX2 selectw -t%0 || exit 1
|
||||
compare menu-target-window-noborder
|
||||
|
||||
# Menu with double border lines.
|
||||
setup 40 14
|
||||
menu -b double -x6 -y8
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
MENU00 abcdefghij
|
||||
MENU01 abcdefghij
|
||||
MENU02 abcdefghij
|
||||
MENU03 abcdefghij
|
||||
MENU04[38;5;250m[48;5;235m Alpha item (a) [39m[49m
|
||||
MENU05[38;5;16m[48;5;220m Beta item (b) [39m[49m
|
||||
MENU06[38;5;45m[48;5;235m [39m[49m
|
||||
MENU07[38;5;250m[48;5;235m Gamma item (g) [39m[49m
|
||||
MENU02[38;5;45m[48;5;235m Menu [39m[49m
|
||||
MENU03[38;5;45m[48;5;235m [38;5;250m Alpha item (a) [38;5;45m [39m[49m
|
||||
MENU04[38;5;45m[48;5;235m [38;5;16m[48;5;220m Beta item (b) [38;5;45m[48;5;235m [39m[49m
|
||||
MENU05[38;5;45m[48;5;235m [39m[49m
|
||||
MENU06[38;5;45m[48;5;235m [38;5;250m Gamma item (g) [38;5;45m [39m[49m
|
||||
MENU07[38;5;45m[48;5;235m [39m[49m
|
||||
MENU08 abcdefghij
|
||||
MENU09 abcdefghij
|
||||
MENU10 abcdefghij
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
MENU00 abcdefghij
|
||||
MENU01 abcdefghij
|
||||
MENU02 abcdefghij
|
||||
MENU03 abcdefghij
|
||||
MENU04 abcd[38;5;250m[48;5;235m Alpha item (a) [39m[49m
|
||||
MENU05 abcd[38;5;16m[48;5;220m Beta item (b) [39m[49m
|
||||
MENU06 abcd[38;5;45m[48;5;235m [39m[49m
|
||||
MENU07 abcd[38;5;250m[48;5;235m Gamma item (g) [39m[49m
|
||||
MENU08 abcdefghij
|
||||
MENU09 abcdefghij
|
||||
MENU10 abcdefghij
|
||||
MENU11 abcdefghij
|
||||
MENU12 abcdefghij
|
||||
@@ -95,8 +95,7 @@ layout_tee_down() { # top/bottom, bottom split: a top tee
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
# Keep the anchor window alive until cleanup, even on slow test runs.
|
||||
$TMUX2 new -d -x40 -y14 'exec cat' || exit 1
|
||||
$TMUX2 new -d -x40 -y14 "sh -c 'exec sleep 100'" || exit 1
|
||||
$TMUX2 set -g status off || exit 1
|
||||
$TMUX2 set -g window-size manual || exit 1
|
||||
$TMUX2 set -g pane-border-format " #{pane_index} " || exit 1
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Switching windows in the same session must redraw the attached client. The
|
||||
# session object is shared, so its current winlink cannot be compared after it
|
||||
# has already been changed.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
INNER="$TEST_TMUX -Lswitch-redraw-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lswitch-redraw-outer-$$ -f/dev/null"
|
||||
CAPTURE=$DIR/capture
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
[ -s "$CAPTURE" ] && cat "$CAPTURE" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
wait_for_client()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
CLIENT=$($INNER list-clients -F '#{client_name}' 2>/dev/null)
|
||||
[ -n "$CLIENT" ] && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "inner client did not attach"
|
||||
}
|
||||
|
||||
wait_outer_has()
|
||||
{
|
||||
marker=$1
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||
grep -q "$marker" "$CAPTURE" && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "outer client did not show $marker"
|
||||
}
|
||||
|
||||
$INNER new-session -d -s inner -x 40 -y 8 \
|
||||
"printf '\033[2J\033[HA-WINDOW'; exec sleep 100" || exit 1
|
||||
$INNER new-window -d -t inner:1 \
|
||||
"printf '\033[2J\033[HB-WINDOW'; exec sleep 100" || exit 1
|
||||
$INNER set-option -g status off || exit 1
|
||||
$INNER set-option -g window-size manual || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 40 -y 8 'sleep 100' || exit 1
|
||||
$OUTER set-option -g status off || exit 1
|
||||
$OUTER set-option -g window-size manual || exit 1
|
||||
$OUTER respawn-pane -k -t outer:0.0 \
|
||||
"$TEST_TMUX -Lswitch-redraw-inner-$$ -f/dev/null attach-session -t inner:0" ||
|
||||
exit 1
|
||||
|
||||
wait_for_client
|
||||
wait_outer_has A-WINDOW
|
||||
|
||||
$INNER switch-client -c "$CLIENT" -t inner:1.0 || exit 1
|
||||
[ "$($INNER display-message -p -t inner '#{window_index}')" -eq 1 ] ||
|
||||
fail "server did not select window 1"
|
||||
wait_outer_has B-WINDOW
|
||||
|
||||
exit 0
|
||||
@@ -1,116 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# When only some lines are redrawn after a synchronized update, a line
|
||||
# following a wrapped line must be drawn at its own position, not where the
|
||||
# cursor happens to be.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
TMUX_TMPDIR=$DIR
|
||||
export TMUX_TMPDIR
|
||||
|
||||
INNER="$TEST_TMUX -Li$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lo$$ -f/dev/null"
|
||||
CONTROL=$DIR/control
|
||||
EMITTER=$DIR/emitter.pl
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
wait_for_file()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ] && [ ! -e "$1" ]; do
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
[ -e "$1" ] || fail "$2"
|
||||
}
|
||||
|
||||
wait_for_client()
|
||||
{
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$INNER list-clients -F '#{client_termfeatures}' 2>/dev/null |
|
||||
grep -q 'sync' && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "sync-capable client did not attach"
|
||||
}
|
||||
|
||||
cat >"$EMITTER" <<'PERL'
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my $control = $ENV{CONTROL};
|
||||
(my $dir = $control) =~ s{/[^/]+$}{};
|
||||
|
||||
# Fill the screen with 80 column lines and no newlines so every line wraps.
|
||||
my $out = "\e[H\e[2J";
|
||||
for my $row (1 .. 24) {
|
||||
$out .= substr(sprintf("ROW%02d_", $row) . ('x' x 80), 0, 80);
|
||||
}
|
||||
syswrite STDOUT, $out . "\e[1;1H";
|
||||
|
||||
open my $painted, '>', "$dir/painted" or die "$dir/painted: $!\n";
|
||||
close $painted;
|
||||
while (!-e $control) {
|
||||
select undef, undef, undef, 0.01;
|
||||
}
|
||||
|
||||
# Change rows 2 and 6 in one synchronized update.
|
||||
my $frame = "\e[?2026h" .
|
||||
"\e[2;1H" . substr("NEW02_" . ('#' x 80), 0, 80) .
|
||||
"\e[6;1H" . substr("NEW06_" . ('%' x 80), 0, 80) .
|
||||
"\e[24;1H" .
|
||||
"\e[?2026l";
|
||||
my $written = syswrite STDOUT, $frame;
|
||||
die "short synchronized frame write\n"
|
||||
unless defined $written && $written == length $frame;
|
||||
open my $done, '>', "$dir/done" or die "$dir/done: $!\n";
|
||||
close $done;
|
||||
select undef, undef, undef, 3;
|
||||
PERL
|
||||
|
||||
$INNER new-session -d -s inner -x 80 -y 24 \
|
||||
"CONTROL='$CONTROL' perl '$EMITTER'" || exit 1
|
||||
$INNER set-option -g status off || exit 1
|
||||
$INNER set-option -g window-size manual || exit 1
|
||||
$INNER set-option -as terminal-features '*:sync' || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 80 -y 24 \
|
||||
"$TEST_TMUX -Li$$ -f/dev/null attach-session -t inner" ||
|
||||
exit 1
|
||||
$OUTER set-option -g status off || exit 1
|
||||
$OUTER set-option -g window-size manual || exit 1
|
||||
wait_for_client
|
||||
|
||||
wait_for_file "$DIR/painted" "application did not paint"
|
||||
sleep 1
|
||||
: >"$CONTROL"
|
||||
wait_for_file "$DIR/done" "application did not finish"
|
||||
sleep 1
|
||||
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$DIR/screen" || exit 1
|
||||
sed -n 2p "$DIR/screen" | grep -q '^NEW02_#' || fail "row 2 not redrawn"
|
||||
sed -n 3p "$DIR/screen" | grep -q '^ROW03_x' || fail "row 3 overwritten"
|
||||
sed -n 6p "$DIR/screen" | grep -q '^NEW06_%' || fail "row 6 not redrawn"
|
||||
exit 0
|
||||
+10
-51
@@ -9,37 +9,14 @@ $TMUX kill-server 2>/dev/null
|
||||
TMUX2="$TEST_TMUX -LtestB$$ -f/dev/null"
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp) || exit 1
|
||||
prompt_pid=
|
||||
|
||||
cleanup () {
|
||||
if [ -n "$prompt_pid" ]; then
|
||||
kill "$prompt_pid" 2>/dev/null
|
||||
wait "$prompt_pid" 2>/dev/null
|
||||
fi
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
rm -f "$TMP"
|
||||
}
|
||||
trap cleanup 0
|
||||
trap 'exit 1' 1 2 15
|
||||
|
||||
fail () {
|
||||
echo "[FAIL] $*" >&2
|
||||
exit 1
|
||||
}
|
||||
TMP=$(mktemp)
|
||||
trap "rm -f $TMP" 0 1 15
|
||||
|
||||
$TMUX2 -f/dev/null new -d || exit 1
|
||||
$TMUX -f/dev/null new -d "$TMUX2 attach" || exit 1
|
||||
i=0
|
||||
while [ -z "$($TMUX2 list-clients -F '#{client_name}')" ]; do
|
||||
[ "$i" -lt 100 ] || fail "inner client did not attach"
|
||||
sleep 0.05
|
||||
i=$((i + 1))
|
||||
done
|
||||
sleep 1
|
||||
|
||||
exit_status=0
|
||||
assertion=0
|
||||
|
||||
format_string () {
|
||||
case $1 in
|
||||
@@ -57,33 +34,12 @@ assert_key () {
|
||||
expected_name=$2
|
||||
format_string=$(format_string "$expected_name")
|
||||
|
||||
# Use a different prompt each time so a stale redraw cannot look ready.
|
||||
assertion=$((assertion + 1))
|
||||
prompt="tty-keys-$assertion:"
|
||||
$TMUX2 command-prompt -k -p "$prompt" \
|
||||
'display-message -pl '"$format_string" > "$TMP" &
|
||||
prompt_pid=$!
|
||||
i=0
|
||||
while ! $TMUX capture-pane -p | grep -Fq "$prompt"; do
|
||||
kill -0 "$prompt_pid" 2>/dev/null || \
|
||||
fail "$keys -> $expected_name: prompt exited before becoming ready"
|
||||
[ "$i" -lt 100 ] || \
|
||||
fail "$keys -> $expected_name: timed out waiting for prompt"
|
||||
sleep 0.05
|
||||
i=$((i + 1))
|
||||
done
|
||||
$TMUX2 command-prompt -k 'display-message -pl '"$format_string" > "$TMP" &
|
||||
sleep 0.05
|
||||
|
||||
$TMUX send-keys $keys || fail "could not send $keys"
|
||||
$TMUX send-keys $keys
|
||||
|
||||
i=0
|
||||
while kill -0 "$prompt_pid" 2>/dev/null; do
|
||||
[ "$i" -lt 100 ] || \
|
||||
fail "$keys -> $expected_name: timed out waiting for key"
|
||||
sleep 0.05
|
||||
i=$((i + 1))
|
||||
done
|
||||
wait "$prompt_pid" || fail "$keys -> $expected_name: prompt failed"
|
||||
prompt_pid=
|
||||
wait
|
||||
|
||||
keys=$(printf '%s' "$keys" | sed -e 's/Escape/\\\\033/g' | tr -d '[:space:]')
|
||||
actual_name=$(tr -d '[:space:]' < "$TMP")
|
||||
@@ -409,4 +365,7 @@ assert_key 'Escape [32;2u' 'S-Space'
|
||||
assert_key 'Escape [9;5u' 'C-Tab'
|
||||
assert_key 'Escape [1;5Z' 'C-S-Tab'
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
exit $exit_status
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# With left/right margins, a pane narrowed by a scrollbar can scroll without
|
||||
# retransmitting its existing rows. Both paths must produce the same screen.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
INNER="$TEST_TMUX -Lmarginsscrollbar-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lmarginsscrollbar-outer-$$ -f/dev/null"
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
cat >"$DIR/emitter.pl" <<'PERL'
|
||||
use strict;
|
||||
use warnings;
|
||||
$| = 1;
|
||||
print join("\r\n", map { "KEEP0$_" } 1 .. 6);
|
||||
while (!-e $ENV{TRIGGER}) {
|
||||
select undef, undef, undef, 0.01;
|
||||
}
|
||||
print "\r\nNEWROW";
|
||||
sleep 100;
|
||||
PERL
|
||||
|
||||
for margins in on off; do
|
||||
$INNER new-session -d -s inner -x 40 -y 6 \
|
||||
"TRIGGER='$DIR/trigger-$margins' perl '$DIR/emitter.pl'" || exit 1
|
||||
$INNER set -g status off || exit 1
|
||||
$INNER set -g status-interval 0 || exit 1
|
||||
$INNER set -g automatic-rename off || exit 1
|
||||
$INNER set -g window-size manual || exit 1
|
||||
$INNER set -g pane-scrollbars on || exit 1
|
||||
if [ "$margins" = on ]; then
|
||||
$INNER set -as terminal-features ',screen-256color:margins' || exit 1
|
||||
fi
|
||||
|
||||
$OUTER new-session -d -s outer -x 40 -y 6 'sleep 100' || exit 1
|
||||
$OUTER set -g status off || exit 1
|
||||
$OUTER set -g window-size manual || exit 1
|
||||
$OUTER set -g default-terminal screen-256color || exit 1
|
||||
$OUTER respawn-pane -k -t outer:0.0 "$INNER attach -t inner" || exit 1
|
||||
sleep 0.5
|
||||
$OUTER pipe-pane -O -t outer:0.0 "cat >'$DIR/output-$margins'" || exit 1
|
||||
$INNER refresh-client || exit 1
|
||||
sleep 0.5
|
||||
grep -aq KEEP02 "$DIR/output-$margins" || fail "initial rows not captured"
|
||||
offset=$(wc -c <"$DIR/output-$margins")
|
||||
: >"$DIR/trigger-$margins"
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
$OUTER capture-pane -p -t outer:0.0 >"$DIR/screen-$margins" || exit 1
|
||||
grep -q NEWROW "$DIR/screen-$margins" && break
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
[ "$i" -lt 50 ] || fail "$margins: scroll did not reach the terminal"
|
||||
sleep 0.2
|
||||
tail -c +"$((offset + 1))" "$DIR/output-$margins" >"$DIR/scroll-$margins"
|
||||
grep -aq NEWROW "$DIR/scroll-$margins" || fail "scroll output not captured"
|
||||
if [ "$margins" = on ]; then
|
||||
if grep -aq KEEP02 "$DIR/scroll-$margins"; then
|
||||
fail "scroll with margins retransmitted an existing row"
|
||||
fi
|
||||
else
|
||||
grep -aq KEEP02 "$DIR/scroll-$margins" ||
|
||||
fail "scroll without margins did not exercise the redraw fallback"
|
||||
fi
|
||||
printf 'KEEP02\nKEEP03\nKEEP04\nKEEP05\nKEEP06\nNEWROW\n' >"$DIR/expected"
|
||||
cmp -s "$DIR/expected" "$DIR/screen-$margins" ||
|
||||
fail "$margins: terminal did not contain the expected scrolled rows"
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
done
|
||||
|
||||
exit 0
|
||||
@@ -1,164 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# A disconnected waiter must release both its wait and its command queue.
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
export TERM
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
OUT=$(mktemp -d)
|
||||
TMUX="$TEST_TMUX -S$OUT/socket -f/dev/null"
|
||||
pids=
|
||||
|
||||
cleanup()
|
||||
{
|
||||
for pid in $pids; do
|
||||
kill "$pid" 2>/dev/null || true
|
||||
done
|
||||
$TMUX kill-server 2>/dev/null || true
|
||||
rm -rf "$OUT"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
wait_list()
|
||||
{
|
||||
expected=$1
|
||||
shift
|
||||
i=0
|
||||
while [ $i -lt 100 ]; do
|
||||
value=$($TMUX wait-for -l "$@") || fail "list waiters failed"
|
||||
[ "$value" = "$expected" ] && return
|
||||
i=$((i + 1))
|
||||
sleep 0.05
|
||||
done
|
||||
fail "expected waiters '$expected', got '$value'"
|
||||
}
|
||||
|
||||
wait_free()
|
||||
{
|
||||
client_pid=$1
|
||||
i=0
|
||||
while [ $i -lt 100 ]; do
|
||||
awk -v pid="$client_pid" '
|
||||
$4 == "IDENTIFY_CLIENTPID" && $5 == pid { address = $3 }
|
||||
address != "" && $2 == "free" && $3 == "client" &&
|
||||
$4 == address && $5 == "(0" { freed = 1 }
|
||||
END { exit !freed }
|
||||
' "$OUT"/tmux-server-*.log && return
|
||||
i=$((i + 1))
|
||||
sleep 0.05
|
||||
done
|
||||
fail "client $client_pid was not freed"
|
||||
}
|
||||
|
||||
cd "$OUT" || exit 1
|
||||
$TMUX -vv new-session -d -s test || fail "new-session failed"
|
||||
$TMUX set -g @continued 0 || fail "set option failed"
|
||||
|
||||
for signal in TERM KILL; do
|
||||
for kind in channel lock event; do
|
||||
name="$kind-$signal"
|
||||
case $kind in
|
||||
channel) args="$name"; list_args="$name" ;;
|
||||
lock)
|
||||
$TMUX wait-for -L "$name" || fail "lock failed"
|
||||
args="-L $name"; list_args="$name"
|
||||
;;
|
||||
event) args="-E @$name"; list_args="-E @$name" ;;
|
||||
esac
|
||||
|
||||
$TMUX wait-for $args \; set -g @continued 1 &
|
||||
dead=$!
|
||||
pids="$dead"
|
||||
wait_list "client-$dead" $list_args
|
||||
|
||||
$TMUX wait-for $args &
|
||||
live=$!
|
||||
pids="$dead $live"
|
||||
wait_list "$(printf 'client-%s\nclient-%s' "$dead" "$live")" \
|
||||
$list_args
|
||||
|
||||
kill -"$signal" "$dead" || fail "kill failed"
|
||||
wait "$dead" 2>/dev/null || true
|
||||
pids="$live"
|
||||
wait_list "client-$live" $list_args
|
||||
wait_free "$dead"
|
||||
[ "$($TMUX show -gqv @continued)" = 0 ] ||
|
||||
fail "disconnected client's next command ran"
|
||||
|
||||
case $kind in
|
||||
channel) $TMUX wait-for -S "$name" || fail "signal failed" ;;
|
||||
lock) $TMUX wait-for -U "$name" || fail "unlock failed" ;;
|
||||
event)
|
||||
$TMUX wait-for -E -w "client-$live" "@$name" ||
|
||||
fail "wake event waiter failed"
|
||||
;;
|
||||
esac
|
||||
wait_list "" $list_args
|
||||
wait_free "$live"
|
||||
wait "$live" || fail "surviving waiter failed"
|
||||
pids=
|
||||
if [ "$kind" = lock ]; then
|
||||
$TMUX wait-for -U "$name" || fail "final unlock failed"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Removing the last waiter must remove its otherwise unused channel, and must
|
||||
# not create a pending signal. Unrelated pending signals must be preserved.
|
||||
$TMUX wait-for -S saved || fail "save signal failed"
|
||||
$TMUX wait-for last &
|
||||
dead=$!
|
||||
pids="$dead"
|
||||
wait_list "client-$dead" last
|
||||
kill -KILL "$dead" || fail "kill last waiter failed"
|
||||
wait "$dead" 2>/dev/null || true
|
||||
pids=
|
||||
wait_list "" last
|
||||
wait_free "$dead"
|
||||
grep -F 'remove empty wait channel last' "$OUT"/tmux-server-*.log \
|
||||
>/dev/null || fail "empty channel was not removed"
|
||||
|
||||
$TMUX wait-for saved \; wait-for last &
|
||||
live=$!
|
||||
pids="$live"
|
||||
wait_list "client-$live" last
|
||||
$TMUX wait-for -S last || fail "signal last failed"
|
||||
wait_free "$live"
|
||||
wait "$live" || fail "pending signal was not preserved"
|
||||
pids=
|
||||
|
||||
# Source-file leaves a cleanup callback behind the blocked command.
|
||||
printf 'wait-for sourced\nset -g @continued 1\n' >"$OUT/source.conf"
|
||||
$TMUX source-file "$OUT/source.conf" &
|
||||
dead=$!
|
||||
pids="$dead"
|
||||
wait_list "client-$dead" sourced
|
||||
kill -KILL "$dead" || fail "kill source-file waiter failed"
|
||||
wait "$dead" 2>/dev/null || true
|
||||
pids=
|
||||
wait_list "" sourced
|
||||
wait_free "$dead"
|
||||
[ "$($TMUX show -gqv @continued)" = 0 ] ||
|
||||
fail "disconnected source-file client's next command ran"
|
||||
|
||||
# Firing an event after cancellation must not call the removed event sink.
|
||||
$TMUX wait-for -E window-renamed &
|
||||
dead=$!
|
||||
pids="$dead"
|
||||
wait_list "client-$dead" -E window-renamed
|
||||
kill -KILL "$dead" || fail "kill event waiter failed"
|
||||
wait "$dead" 2>/dev/null || true
|
||||
pids=
|
||||
wait_list "" -E window-renamed
|
||||
wait_free "$dead"
|
||||
$TMUX rename-window -t test:0 renamed || fail "rename-window failed"
|
||||
$TMUX display-message -p '#{pid}' >/dev/null || fail "server exited"
|
||||
|
||||
exit 0
|
||||
@@ -1,123 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Dragging an inactive pane's scrollbar slider changes which pane is
|
||||
# active (window_copy_scroll(), window-copy.c). Every other place in the
|
||||
# codebase that changes the active pane pairs it with
|
||||
# window_redraw_active_switch() (or falls back to a full window redraw) so
|
||||
# the old/new active panes' window-active-style/window-style colours get
|
||||
# repainted - window_copy_scroll() was the one caller that did neither, so
|
||||
# a scrollbar-slider drag changed focus but left both panes' body colours
|
||||
# stale. This used to be masked by window_set_active_pane() itself doing
|
||||
# an unconditional full redraw on every active-pane change, until that was
|
||||
# narrowed to borders/status-only for the common case.
|
||||
#
|
||||
# This creates two tiled panes with clearly distinguishable
|
||||
# window-active-style/window-style backgrounds, puts the *inactive* one in
|
||||
# copy mode, drags its scrollbar slider, and checks - via an attached
|
||||
# client's own received bytes, since window-style is applied during
|
||||
# redraw composition rather than stored in the grid, so capture-pane
|
||||
# alone would not reflect it - that the newly active pane's marker text
|
||||
# immediately shows the active-style colour, not the stale one.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
LC_ALL=C.UTF-8
|
||||
export PATH TERM LC_ALL
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
|
||||
DIR=$(mktemp -d) || exit 1
|
||||
INNER="$TEST_TMUX -Lsbfocus-inner-$$ -f/dev/null"
|
||||
OUTER="$TEST_TMUX -Lsbfocus-outer-$$ -f/dev/null"
|
||||
CAPTURE=$DIR/capture
|
||||
ESC=$(printf '\033')
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*" >&2
|
||||
[ -s "$CAPTURE" ] && cat -A "$CAPTURE" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$OUTER kill-server 2>/dev/null
|
||||
$INNER kill-server 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
get_mark_color()
|
||||
{
|
||||
$OUTER capture-pane -e -p -t outer:0.0 >"$CAPTURE" 2>/dev/null || true
|
||||
LC_ALL=C grep -a "MARK1" "$CAPTURE" |
|
||||
LC_ALL=C grep -aoE "${ESC}\[4[12]m" | tail -1
|
||||
}
|
||||
|
||||
wait_mark_color()
|
||||
{
|
||||
want=$1
|
||||
i=0
|
||||
while [ "$i" -lt 50 ]; do
|
||||
[ "$(get_mark_color)" = "$want" ] && return 0
|
||||
sleep 0.1
|
||||
i=$((i + 1))
|
||||
done
|
||||
fail "MARK1's pane never showed the expected background (wanted '$want', got '$(get_mark_color)')"
|
||||
}
|
||||
|
||||
mouse()
|
||||
{
|
||||
sequence=$(printf '\033[<%s;%s;%s%s' "$1" "$2" "$3" "$4")
|
||||
$OUTER send-keys -t outer:0.0 -l "$sequence" || exit 1
|
||||
sleep 0.1
|
||||
}
|
||||
|
||||
$INNER new-session -d -s inner -x 40 -y 10 'sleep 100' || exit 1
|
||||
$INNER set-option -g status off || exit 1
|
||||
$INNER set-option -g window-size manual || exit 1
|
||||
$INNER set-option -g mouse on || exit 1
|
||||
$INNER set-option -g pane-scrollbars on || exit 1
|
||||
$INNER setw pane-scrollbars-position right || exit 1
|
||||
$INNER setw pane-scrollbars-style 'width=1,pad=0' || exit 1
|
||||
$INNER set-option -g window-active-style 'bg=red' || exit 1
|
||||
$INNER set-option -g window-style 'bg=green' || exit 1
|
||||
$INNER split-window -h -t inner -d 'printf MARK1; sleep 100' || exit 1
|
||||
|
||||
PANE1=$($INNER list-panes -t inner -F '#{pane_id}' | sed -n 2p)
|
||||
[ "$($INNER display-message -p -t "$PANE1" '#{pane_active}')" = 0 ] ||
|
||||
fail "sanity: MARK1's pane is already active before the drag"
|
||||
|
||||
# Put the inactive pane in copy mode without switching focus (copy-mode -t
|
||||
# targets a pane without making it active).
|
||||
$INNER copy-mode -t "$PANE1" || exit 1
|
||||
|
||||
$OUTER new-session -d -s outer -x 40 -y 10 'sleep 100' || exit 1
|
||||
$OUTER set-option -g status off || exit 1
|
||||
$OUTER set-option -g window-size manual || exit 1
|
||||
$OUTER set-option -g default-terminal screen-256color || exit 1
|
||||
$OUTER respawn-pane -k -t outer:0.0 \
|
||||
"$TEST_TMUX -Lsbfocus-inner-$$ -f/dev/null attach-session -t inner" ||
|
||||
exit 1
|
||||
|
||||
wait_mark_color "${ESC}[42m"
|
||||
|
||||
XOFF=$($INNER display-message -p -t "$PANE1" '#{pane_left}')
|
||||
YOFF=$($INNER display-message -p -t "$PANE1" '#{pane_top}')
|
||||
SX=$($INNER display-message -p -t "$PANE1" '#{pane_width}')
|
||||
SBCOL=$((XOFF + SX + 1))
|
||||
SBROW=$((YOFF + 1))
|
||||
|
||||
# With no scrollback beyond the pane's own content, the slider fills the
|
||||
# whole scrollbar track, so any point on it (here, its very first row) is
|
||||
# on the slider.
|
||||
mouse 0 "$SBCOL" "$SBROW" M
|
||||
mouse 32 "$SBCOL" "$((SBROW + 1))" M
|
||||
mouse 0 "$SBCOL" "$((SBROW + 1))" m
|
||||
|
||||
[ "$($INNER display-message -p -t "$PANE1" '#{pane_active}')" = 1 ] ||
|
||||
fail "sanity: dragging the scrollbar slider did not change focus"
|
||||
|
||||
wait_mark_color "${ESC}[41m"
|
||||
|
||||
exit 0
|
||||
+17
-288
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: screen-redraw.c,v 1.161 2026/09/22 16:56:07 nicm Exp $ */
|
||||
/* $OpenBSD: screen-redraw.c,v 1.160 2026/09/21 10:22:31 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -204,36 +204,14 @@ struct redraw_scene {
|
||||
u_int oy;
|
||||
};
|
||||
|
||||
/* A damaged rectangle in a window. */
|
||||
struct redraw_damage {
|
||||
u_int x;
|
||||
u_int y;
|
||||
u_int sx;
|
||||
u_int sy;
|
||||
|
||||
TAILQ_ENTRY(redraw_damage) entry;
|
||||
};
|
||||
|
||||
/*
|
||||
* If there are more damage rectangles than this, they are collapsed into
|
||||
* one.
|
||||
*/
|
||||
#define REDRAW_DAMAGE_MAX 16
|
||||
|
||||
/* Cell for building the scene. */
|
||||
struct redraw_build_cell {
|
||||
struct redraw_span_data data;
|
||||
};
|
||||
|
||||
static struct redraw_build_cell *redraw_cells;
|
||||
static size_t redraw_ncells;
|
||||
|
||||
/*
|
||||
* We can reuse the same pane status lines during one damage redraw, but when
|
||||
* we enter a new one, the client or format variables may have changed, so we
|
||||
* need to make them again. The generation is increased so this happens.
|
||||
*/
|
||||
static u_int redraw_status_generation;
|
||||
|
||||
/* Context for building the scene. */
|
||||
struct redraw_build_ctx {
|
||||
struct client *c;
|
||||
@@ -1076,13 +1054,6 @@ redraw_free_scene(struct redraw_scene *scene)
|
||||
free(scene);
|
||||
}
|
||||
|
||||
/* Does a client's cached scene show this window? */
|
||||
int
|
||||
redraw_client_has_window(struct client *c, struct window *w)
|
||||
{
|
||||
return (c->redraw_scene != NULL && c->redraw_scene->w == w);
|
||||
}
|
||||
|
||||
/* Mark a window's cached redraw scenes as out of date. */
|
||||
void
|
||||
redraw_invalidate_scene(struct window *w)
|
||||
@@ -1090,104 +1061,6 @@ redraw_invalidate_scene(struct window *w)
|
||||
w->redraw_scene_generation++;
|
||||
}
|
||||
|
||||
/* Free all pending damage for a window. */
|
||||
void
|
||||
redraw_free_damage(struct window *w)
|
||||
{
|
||||
struct redraw_damage *rd, *rd1;
|
||||
|
||||
TAILQ_FOREACH_SAFE(rd, &w->damage, entry, rd1) {
|
||||
TAILQ_REMOVE(&w->damage, rd, entry);
|
||||
free(rd);
|
||||
}
|
||||
w->damage_count = 0;
|
||||
}
|
||||
|
||||
/* Collapse all pending damage for a window into one rectangle. */
|
||||
static void
|
||||
redraw_collapse_damage(struct window *w)
|
||||
{
|
||||
struct redraw_damage *rd, *rd1, *first;
|
||||
u_int x0, y0, x1, y1;
|
||||
|
||||
first = TAILQ_FIRST(&w->damage);
|
||||
if (first == NULL)
|
||||
return;
|
||||
x0 = first->x;
|
||||
y0 = first->y;
|
||||
x1 = first->x + first->sx;
|
||||
y1 = first->y + first->sy;
|
||||
|
||||
TAILQ_FOREACH_SAFE(rd, &w->damage, entry, rd1) {
|
||||
if (rd->x < x0)
|
||||
x0 = rd->x;
|
||||
if (rd->y < y0)
|
||||
y0 = rd->y;
|
||||
if (rd->x + rd->sx > x1)
|
||||
x1 = rd->x + rd->sx;
|
||||
if (rd->y + rd->sy > y1)
|
||||
y1 = rd->y + rd->sy;
|
||||
if (rd != first) {
|
||||
TAILQ_REMOVE(&w->damage, rd, entry);
|
||||
free(rd);
|
||||
}
|
||||
}
|
||||
|
||||
first->x = x0;
|
||||
first->y = y0;
|
||||
first->sx = x1 - x0;
|
||||
first->sy = y1 - y0;
|
||||
w->damage_count = 1;
|
||||
}
|
||||
|
||||
/* Record window damage, merging nearby rectangles and limiting the count. */
|
||||
void
|
||||
redraw_damage_window(struct window *w, u_int x, u_int y, u_int sx, u_int sy)
|
||||
{
|
||||
struct redraw_damage *rd;
|
||||
u_int x0, y0, x1, y1, area, union_area;
|
||||
|
||||
if (x >= w->sx || y >= w->sy)
|
||||
return;
|
||||
if (x + sx > w->sx)
|
||||
sx = w->sx - x;
|
||||
if (y + sy > w->sy)
|
||||
sy = w->sy - y;
|
||||
if (sx == 0 || sy == 0)
|
||||
return;
|
||||
|
||||
TAILQ_FOREACH(rd, &w->damage, entry) {
|
||||
if (x > rd->x + rd->sx || rd->x > x + sx ||
|
||||
y > rd->y + rd->sy || rd->y > y + sy)
|
||||
continue;
|
||||
|
||||
x0 = (x < rd->x) ? x : rd->x;
|
||||
y0 = (y < rd->y) ? y : rd->y;
|
||||
x1 = (x + sx > rd->x + rd->sx) ? x + sx : rd->x + rd->sx;
|
||||
y1 = (y + sy > rd->y + rd->sy) ? y + sy : rd->y + rd->sy;
|
||||
|
||||
area = sx * sy + rd->sx * rd->sy;
|
||||
union_area = (x1 - x0) * (y1 - y0);
|
||||
if (union_area > 2 * area)
|
||||
continue;
|
||||
|
||||
rd->x = x0;
|
||||
rd->y = y0;
|
||||
rd->sx = x1 - x0;
|
||||
rd->sy = y1 - y0;
|
||||
return;
|
||||
}
|
||||
|
||||
rd = xcalloc(1, sizeof *rd);
|
||||
rd->x = x;
|
||||
rd->y = y;
|
||||
rd->sx = sx;
|
||||
rd->sy = sy;
|
||||
TAILQ_INSERT_TAIL(&w->damage, rd, entry);
|
||||
if (++w->damage_count > REDRAW_DAMAGE_MAX)
|
||||
redraw_collapse_damage(w);
|
||||
}
|
||||
|
||||
/* Mark all cached redraw scenes as out of date. */
|
||||
void
|
||||
redraw_invalidate_all_scenes(void)
|
||||
@@ -1730,24 +1603,6 @@ redraw_set_draw_context(struct redraw_draw_ctx *dctx,
|
||||
dctx->flags |= REDRAW_ISOLATES;
|
||||
}
|
||||
|
||||
/* Build a pane prompt. */
|
||||
static void
|
||||
redraw_make_pane_prompt(struct window_pane *wp, struct screen *screen)
|
||||
{
|
||||
struct screen_write_ctx ctx;
|
||||
struct prompt_draw_data pdd;
|
||||
|
||||
screen_init(screen, wp->sx, 1, 0);
|
||||
screen_write_start(&ctx, screen);
|
||||
pdd.ctx = &ctx;
|
||||
pdd.cursor_x = &wp->prompt_cx;
|
||||
pdd.area_x = 0;
|
||||
pdd.area_width = wp->sx;
|
||||
pdd.prompt_line = 0;
|
||||
prompt_draw(wp->prompt, &pdd);
|
||||
screen_write_stop(&ctx);
|
||||
}
|
||||
|
||||
/* Draw a pane's prompt over its content. */
|
||||
static void
|
||||
redraw_draw_pane_prompt(struct redraw_draw_ctx *dctx, struct window_pane *wp)
|
||||
@@ -1756,6 +1611,8 @@ redraw_draw_pane_prompt(struct redraw_draw_ctx *dctx, struct window_pane *wp)
|
||||
struct client *c = scene->c;
|
||||
struct tty *tty = &c->tty;
|
||||
struct screen screen;
|
||||
struct screen_write_ctx ctx;
|
||||
struct prompt_draw_data pdd;
|
||||
int ox = scene->ox, oy = scene->oy;
|
||||
int sx = scene->sx, sy = scene->sy;
|
||||
int line, cy, px, offset, width, wy;
|
||||
@@ -1788,8 +1645,17 @@ redraw_draw_pane_prompt(struct redraw_draw_ctx *dctx, struct window_pane *wp)
|
||||
if (px + width > sx)
|
||||
width = sx - px;
|
||||
|
||||
redraw_make_pane_prompt(wp, &screen);
|
||||
tty_draw_line(tty, &screen, offset, 0, width, px, cy, NULL);
|
||||
screen_init(&screen, wp->sx, 1, 0);
|
||||
screen_write_start(&ctx, &screen);
|
||||
pdd.ctx = &ctx;
|
||||
pdd.cursor_x = &wp->prompt_cx;
|
||||
pdd.area_x = 0;
|
||||
pdd.area_width = wp->sx;
|
||||
pdd.prompt_line = 0;
|
||||
prompt_draw(wp->prompt, &pdd);
|
||||
screen_write_stop(&ctx);
|
||||
|
||||
tty_draw_line(tty, &screen, 0, offset, width, px, cy, NULL);
|
||||
screen_free(&screen);
|
||||
}
|
||||
|
||||
@@ -1818,9 +1684,7 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
|
||||
redraw = status_prompt_redraw(c);
|
||||
else
|
||||
redraw = status_redraw(c);
|
||||
if (!redraw &&
|
||||
(~c->flags & CLIENT_REDRAWSTATUSALWAYS) &&
|
||||
!REDRAW_IS_ALL(flags)) {
|
||||
if (!redraw && !REDRAW_IS_ALL(flags)) {
|
||||
flags &= ~REDRAW_STATUS;
|
||||
if (flags == 0)
|
||||
return;
|
||||
@@ -1978,7 +1842,7 @@ redraw_screen(struct client *c)
|
||||
else {
|
||||
if (c->flags & CLIENT_REDRAWBORDERS)
|
||||
flags |= (REDRAW_PANE_BORDER|REDRAW_PANE_STATUS);
|
||||
if (c->flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))
|
||||
if (c->flags & CLIENT_REDRAWSTATUS)
|
||||
flags |= (REDRAW_STATUS|REDRAW_PANE_STATUS);
|
||||
if (c->flags & CLIENT_REDRAWMENU)
|
||||
flags |= REDRAW_MENU;
|
||||
@@ -2004,138 +1868,3 @@ redraw_pane_scrollbar(struct client *c, struct window_pane *wp)
|
||||
{
|
||||
redraw_draw(c, wp, REDRAW_PANE_SCROLLBAR);
|
||||
}
|
||||
|
||||
/* Rebuild damaged pane status. */
|
||||
static void
|
||||
redraw_damage_refresh_status(struct redraw_draw_ctx *dctx,
|
||||
struct window_pane *wp)
|
||||
{
|
||||
struct redraw_span *first;
|
||||
u_int g = wp->status_generation, width;
|
||||
|
||||
if ((wp->flags & PANE_NEWSTATUS) && g == redraw_status_generation)
|
||||
return;
|
||||
width = redraw_pane_status_width(dctx, wp, &first);
|
||||
if (width != 0) {
|
||||
window_make_pane_status(wp, dctx->scene->c, width, first);
|
||||
wp->flags |= PANE_NEWSTATUS;
|
||||
wp->status_generation = redraw_status_generation;
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw a pane's prompt over a damaged span. */
|
||||
static void
|
||||
redraw_damage_draw_pane_prompt(struct redraw_draw_ctx *dctx,
|
||||
struct redraw_span *span, u_int y)
|
||||
{
|
||||
struct redraw_scene *scene = dctx->scene;
|
||||
struct window_pane *wp = span->data.p.wp;
|
||||
struct tty *tty = &scene->c->tty;
|
||||
struct screen screen;
|
||||
u_int px = span->data.p.px, width, prompt_y;
|
||||
|
||||
if (wp->prompt == NULL || wp->sx == 0 || wp->sy == 0)
|
||||
return;
|
||||
if (dctx->flags & REDRAW_STATUS_TOP)
|
||||
prompt_y = 0;
|
||||
else
|
||||
prompt_y = wp->sy - 1;
|
||||
if (span->data.p.py != prompt_y)
|
||||
return;
|
||||
redraw_make_pane_prompt(wp, &screen);
|
||||
if (px < screen_size_x(&screen)) {
|
||||
width = span->width;
|
||||
if (width > screen_size_x(&screen) - px)
|
||||
width = screen_size_x(&screen) - px;
|
||||
tty_draw_line(tty, &screen, px, 0, width, span->x, y, NULL);
|
||||
}
|
||||
screen_free(&screen);
|
||||
}
|
||||
|
||||
/* Draw the spans intersecting a damaged rectangle. */
|
||||
static void
|
||||
redraw_draw_damage_rectangle(struct redraw_draw_ctx *dctx, u_int x, u_int y,
|
||||
u_int sx, u_int sy)
|
||||
{
|
||||
struct redraw_scene *scene = dctx->scene;
|
||||
struct redraw_line *line;
|
||||
struct redraw_spans *spans;
|
||||
struct redraw_span *span;
|
||||
u_int cy, yy, type;
|
||||
|
||||
if (x >= scene->sx || y >= scene->sy)
|
||||
return;
|
||||
if (x + sx > scene->sx)
|
||||
sx = scene->sx - x;
|
||||
if (y + sy > scene->sy)
|
||||
sy = scene->sy - y;
|
||||
if (sx == 0 || sy == 0)
|
||||
return;
|
||||
|
||||
for (yy = y; yy < y + sy; yy++) {
|
||||
line = &scene->lines[yy];
|
||||
if (dctx->flags & REDRAW_STATUS_TOP)
|
||||
cy = dctx->status_lines + yy;
|
||||
else
|
||||
cy = yy;
|
||||
for (type = 0; type < REDRAW_SPAN_TYPES; type++) {
|
||||
spans = &line->spans[type];
|
||||
TAILQ_FOREACH(span, spans, entry) {
|
||||
if (span->x >= x + sx)
|
||||
continue;
|
||||
if (span->x + span->width <= x)
|
||||
continue;
|
||||
if (type == REDRAW_SPAN_STATUS) {
|
||||
redraw_damage_refresh_status(dctx,
|
||||
span->data.st.wp);
|
||||
}
|
||||
redraw_draw_span(dctx, span, cy);
|
||||
if (type == REDRAW_SPAN_PANE) {
|
||||
redraw_damage_draw_pane_prompt(dctx,
|
||||
span, cy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw pending window damage on this client. */
|
||||
void
|
||||
redraw_client_damage(struct client *c)
|
||||
{
|
||||
struct window *w = c->session->curw->window;
|
||||
struct window_pane *wp;
|
||||
struct redraw_scene *scene;
|
||||
struct redraw_draw_ctx dctx;
|
||||
struct redraw_damage *rd;
|
||||
u_int ox, oy, sx, sy, x0, y0, x1, y1;
|
||||
|
||||
if (TAILQ_EMPTY(&w->damage))
|
||||
return;
|
||||
redraw_status_generation++;
|
||||
|
||||
scene = redraw_get_scene(c);
|
||||
if (scene == NULL)
|
||||
return;
|
||||
redraw_set_draw_context(&dctx, scene);
|
||||
redraw_get_window_offset(c, &ox, &oy, &sx, &sy);
|
||||
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
wp->border_gc_set = 0;
|
||||
wp->active_border_gc_set = 0;
|
||||
}
|
||||
|
||||
tty_sync_start(&c->tty);
|
||||
tty_update_mode(&c->tty, c->tty.mode & ~CURSOR_MODES, NULL);
|
||||
|
||||
TAILQ_FOREACH(rd, &w->damage, entry) {
|
||||
x0 = (rd->x > ox) ? rd->x : ox;
|
||||
y0 = (rd->y > oy) ? rd->y : oy;
|
||||
x1 = (rd->x + rd->sx < ox + sx) ? rd->x + rd->sx : ox + sx;
|
||||
y1 = (rd->y + rd->sy < oy + sy) ? rd->y + rd->sy : oy + sy;
|
||||
if (x0 < x1 && y0 < y1) {
|
||||
redraw_draw_damage_rectangle(&dctx, x0 - ox, y0 - oy,
|
||||
x1 - x0, y1 - y0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
-36
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: screen-write.c,v 1.292 2026/09/21 12:14:32 nicm Exp $ */
|
||||
/* $OpenBSD: screen-write.c,v 1.291 2026/09/21 10:22:31 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -120,26 +120,14 @@ screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy)
|
||||
evtimer_add(&w->offset_timer, &tv);
|
||||
}
|
||||
|
||||
/* Redraw lines. */
|
||||
/* Do a full redraw. */
|
||||
static void
|
||||
screen_write_redraw_cb(const struct tty_ctx *ttyctx, u_int py, u_int ny)
|
||||
screen_write_redraw_cb(const struct tty_ctx *ttyctx)
|
||||
{
|
||||
struct window_pane *wp = ttyctx->arg;
|
||||
int x0, y0, x1, y1;
|
||||
|
||||
if (wp == NULL)
|
||||
return;
|
||||
|
||||
x0 = wp->xoff;
|
||||
y0 = wp->yoff + (int)py;
|
||||
x1 = x0 + (int)wp->sx;
|
||||
y1 = y0 + (int)ny;
|
||||
if (x0 < 0)
|
||||
x0 = 0;
|
||||
if (y0 < 0)
|
||||
y0 = 0;
|
||||
if (x1 > x0 && y1 > y0)
|
||||
redraw_damage_window(wp->window, x0, y0, x1 - x0, y1 - y0);
|
||||
if (wp != NULL)
|
||||
wp->flags |= PANE_REDRAW;
|
||||
}
|
||||
|
||||
/* Update context for client. */
|
||||
@@ -845,39 +833,34 @@ screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice,
|
||||
struct screen *s = ctx->s;
|
||||
struct grid_cell default_gc;
|
||||
const struct grid_cell *gc = &default_gc;
|
||||
u_int border, cx = s->cx, cy = s->cy, i, j, width;
|
||||
u_int cx, cy, i, j, width = menu->width;
|
||||
const char *name;
|
||||
|
||||
cx = s->cx;
|
||||
cy = s->cy;
|
||||
|
||||
memcpy(&default_gc, menu_gc, sizeof default_gc);
|
||||
|
||||
if (lines == BOX_LINES_NONE) {
|
||||
border = 0;
|
||||
width = menu->item_width;
|
||||
} else {
|
||||
border = 1;
|
||||
width = menu->width;
|
||||
screen_write_box(ctx, width + 4, menu->count + 2, lines,
|
||||
border_gc, menu->title);
|
||||
}
|
||||
screen_write_box(ctx, menu->width + 4, menu->count + 2, lines,
|
||||
border_gc, menu->title);
|
||||
|
||||
for (i = 0; i < menu->count; i++) {
|
||||
name = menu->items[i].name;
|
||||
if (name == NULL) {
|
||||
screen_write_cursormove(ctx, cx, cy + border + i, 0);
|
||||
screen_write_hline(ctx, width + 2 + (2 * border), 1, 1,
|
||||
lines, border_gc);
|
||||
screen_write_cursormove(ctx, cx, cy + 1 + i, 0);
|
||||
screen_write_hline(ctx, width + 4, 1, 1, lines,
|
||||
border_gc);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (choice >= 0 && i == (u_int)choice && *name != '-')
|
||||
gc = choice_gc;
|
||||
|
||||
screen_write_cursormove(ctx, cx + border, cy + border + i, 0);
|
||||
screen_write_cursormove(ctx, cx + 1, cy + 1 + i, 0);
|
||||
for (j = 0; j < width + 2; j++)
|
||||
screen_write_putc(ctx, gc, ' ');
|
||||
|
||||
screen_write_cursormove(ctx, cx + border + 1, cy + border + i,
|
||||
0);
|
||||
screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
|
||||
if (*name == '-') {
|
||||
default_gc.attr |= GRID_ATTR_DIM;
|
||||
format_draw(ctx, gc, width, name + 1, NULL, 0);
|
||||
@@ -2167,7 +2150,7 @@ screen_write_fullredraw(struct screen_write_ctx *ctx)
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 1, 0);
|
||||
if (ttyctx.redraw_cb != NULL)
|
||||
ttyctx.redraw_cb(&ttyctx, 0, ttyctx.sy);
|
||||
ttyctx.redraw_cb(&ttyctx);
|
||||
}
|
||||
|
||||
/* Trim collected items. */
|
||||
@@ -3187,7 +3170,7 @@ screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 1, 0);
|
||||
if (ttyctx.redraw_cb != NULL)
|
||||
ttyctx.redraw_cb(&ttyctx, 0, ttyctx.sy);
|
||||
ttyctx.redraw_cb(&ttyctx);
|
||||
}
|
||||
|
||||
/* Turn alternate screen off. */
|
||||
@@ -3212,5 +3195,5 @@ screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 1, 0);
|
||||
if (ttyctx.redraw_cb != NULL)
|
||||
ttyctx.redraw_cb(&ttyctx, 0, ttyctx.sy);
|
||||
ttyctx.redraw_cb(&ttyctx);
|
||||
}
|
||||
|
||||
+11
-35
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: server-client.c,v 1.514 2026/09/22 06:46:50 nicm Exp $ */
|
||||
/* $OpenBSD: server-client.c,v 1.513 2026/09/21 10:22:31 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -356,13 +356,7 @@ server_client_set_session(struct client *c, struct session *s)
|
||||
tty_update_client_offset(c);
|
||||
status_timer_start(c);
|
||||
server_client_fire_session_changed(c, old);
|
||||
|
||||
/*
|
||||
* Redraw if the session or displayed window changed. Use the
|
||||
* cached scene because the session's current window is already set.
|
||||
*/
|
||||
if (old != s || !redraw_client_has_window(c, s->curw->window))
|
||||
server_redraw_client(c);
|
||||
server_redraw_client(c);
|
||||
}
|
||||
|
||||
server_check_unattached();
|
||||
@@ -390,9 +384,6 @@ server_client_lost(struct client *c)
|
||||
TAILQ_REMOVE(&clients, c, entry);
|
||||
log_debug("lost client %p", c);
|
||||
|
||||
cmd_wait_for_client_lost(c);
|
||||
cmdq_next(c);
|
||||
|
||||
if (c->flags & CLIENT_ATTACHED) {
|
||||
server_client_attached_lost(c);
|
||||
events_fire_client("client-detached", c);
|
||||
@@ -1355,11 +1346,10 @@ server_client_key_callback(struct cmdq_item *item, void *data)
|
||||
m->key = key;
|
||||
|
||||
/*
|
||||
* Synchronize direct drag output with the later damage redraw
|
||||
* before invoking the drag callback.
|
||||
* Mouse drag is in progress, so fire the callback (now that
|
||||
* the mouse event is valid).
|
||||
*/
|
||||
if ((key & KEYC_MASK_KEY) == KEYC_DRAGGING) {
|
||||
tty_sync_start(&c->tty);
|
||||
c->tty.mouse_drag_update(c, m);
|
||||
goto out;
|
||||
}
|
||||
@@ -1786,8 +1776,8 @@ server_client_loop(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear window redraw state after processing all clients. Deferred
|
||||
* redraws are preserved in client flags.
|
||||
* Any windows will have been redrawn as part of clients, so clear
|
||||
* their flags now.
|
||||
*/
|
||||
RB_FOREACH(w, windows, &windows) {
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
@@ -1798,8 +1788,6 @@ server_client_loop(void)
|
||||
wp->flags &= ~(PANE_REDRAW|PANE_REDRAWSCROLLBAR|
|
||||
PANE_ACTIVITY);
|
||||
}
|
||||
redraw_free_damage(w);
|
||||
|
||||
check_window_name(w);
|
||||
}
|
||||
|
||||
@@ -2345,8 +2333,6 @@ server_client_any_pane_redraw(struct client *c)
|
||||
|
||||
if (c->flags & CLIENT_REDRAWWINDOW)
|
||||
return (1);
|
||||
if (!TAILQ_EMPTY(&w->damage))
|
||||
return (1);
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
if (wp->flags & (PANE_REDRAW|PANE_REDRAWSCROLLBAR))
|
||||
return (1);
|
||||
@@ -2363,7 +2349,6 @@ server_client_check_redraw(struct client *c)
|
||||
struct window *w = s->curw->window;
|
||||
struct window_pane *wp;
|
||||
int needed, tflags, mode = tty->mode;
|
||||
int damaged = !TAILQ_EMPTY(&w->damage);
|
||||
struct timeval tv = { .tv_usec = 1000 };
|
||||
static struct event ev;
|
||||
size_t n;
|
||||
@@ -2389,12 +2374,12 @@ server_client_check_redraw(struct client *c)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ignore output queued within the current synchronized frame. */
|
||||
/*
|
||||
* If there is outstanding data, defer the redraw until it has been
|
||||
* consumed. We can just add a timer to get out of the event loop and
|
||||
* end up back here.
|
||||
*/
|
||||
n = EVBUFFER_LENGTH(tty->out);
|
||||
if ((tty->flags & TTY_SYNCING) && n > tty->sync_offset)
|
||||
n = tty->sync_offset;
|
||||
|
||||
/* Defer until output drains, preserving damage in client flags. */
|
||||
if (n != 0 || (tty->flags & TTY_BLOCK)) {
|
||||
if (n != 0)
|
||||
log_debug("%s: redraw deferred (%zu left)", c->name, n);
|
||||
@@ -2406,10 +2391,6 @@ server_client_check_redraw(struct client *c)
|
||||
log_debug("redraw timer started");
|
||||
evtimer_add(&ev, &tv);
|
||||
}
|
||||
if (damaged) {
|
||||
c->flags |= CLIENT_REDRAWWINDOW;
|
||||
return;
|
||||
}
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
if (wp->flags & PANE_REDRAW) {
|
||||
c->flags |= CLIENT_REDRAWWINDOW;
|
||||
@@ -2443,10 +2424,6 @@ server_client_check_redraw(struct client *c)
|
||||
redraw_pane_scrollbar(c, wp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw damage here if no client redraw flags will handle it. */
|
||||
if (damaged && (c->flags & CLIENT_ALLREDRAWFLAGS) == 0)
|
||||
redraw_client_damage(c);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2460,7 +2437,6 @@ server_client_check_redraw(struct client *c)
|
||||
}
|
||||
server_client_set_progress_bar(c);
|
||||
redraw_screen(c);
|
||||
redraw_client_damage(c);
|
||||
}
|
||||
|
||||
/* Put the tty back how it was. */
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: server-fn.c,v 1.154 2026/09/21 12:43:36 nicm Exp $ */
|
||||
/* $OpenBSD: server-fn.c,v 1.153 2026/09/10 11:02:18 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -310,8 +310,8 @@ server_link_window(struct session *src, struct winlink *srcwl,
|
||||
}
|
||||
if (killflag) {
|
||||
/*
|
||||
* Can't use session_detach as it won't detach the only
|
||||
* window.
|
||||
* Can't use session_detach as it will destroy session
|
||||
* if this makes it empty.
|
||||
*/
|
||||
events_fire_winlink("window-unlinked", dstwl);
|
||||
dstwl->flags &= ~WINLINK_ALERTFLAGS;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: session.c,v 1.108 2026/09/21 12:43:36 nicm Exp $ */
|
||||
/* $OpenBSD: session.c,v 1.107 2026/08/05 07:35:35 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -336,17 +336,10 @@ session_attach(struct session *s, struct window *w, int idx, char **cause)
|
||||
return (wl);
|
||||
}
|
||||
|
||||
/*
|
||||
* Detach a window from a session. Returns 1 if the window has not been
|
||||
* detached - the caller must destroy the session.
|
||||
*/
|
||||
/* Detach a window from a session. */
|
||||
int
|
||||
session_detach(struct session *s, struct winlink *wl)
|
||||
{
|
||||
if (RB_MIN(winlinks, &s->windows) == wl &&
|
||||
RB_MAX(winlinks, &s->windows) == wl)
|
||||
return (1);
|
||||
|
||||
if (s->curw == wl &&
|
||||
session_last(s) != 0 &&
|
||||
session_previous(s, 0) != 0)
|
||||
@@ -359,6 +352,8 @@ session_detach(struct session *s, struct winlink *wl)
|
||||
|
||||
session_group_synchronize_from(s);
|
||||
|
||||
if (RB_EMPTY(&s->windows))
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: spawn.c,v 1.53 2026/09/21 12:43:36 nicm Exp $ */
|
||||
/* $OpenBSD: spawn.c,v 1.52 2026/08/20 09:19:24 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -166,8 +166,8 @@ spawn_window(struct spawn_context *sc, char **cause)
|
||||
}
|
||||
if (wl != NULL) {
|
||||
/*
|
||||
* Can't use session_detach as it won't detach the only
|
||||
* window.
|
||||
* Can't use session_detach as it will destroy session
|
||||
* if this makes it empty.
|
||||
*/
|
||||
wl->flags &= ~WINLINK_ALERTFLAGS;
|
||||
events_fire_winlink("window-unlinked", wl);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.\" $OpenBSD: tmux.1,v 1.1173 2026/09/23 12:37:50 nicm Exp $
|
||||
.\" $OpenBSD: tmux.1,v 1.1171 2026/09/21 10:22:31 nicm Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
.\"
|
||||
@@ -14,7 +14,7 @@
|
||||
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd $Mdocdate: September 23 2026 $
|
||||
.Dd $Mdocdate: September 21 2026 $
|
||||
.Dt TMUX 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -994,19 +994,13 @@ Will run:
|
||||
/bin/sh \-c \[aq]vi \[ti]/.tmux.conf\[aq]
|
||||
.Ed
|
||||
.Pp
|
||||
Unless specified, tmux uses the value of
|
||||
.Ic default\-shell
|
||||
in place of
|
||||
.Pa /bin/sh .
|
||||
.Pp
|
||||
Additionally, the
|
||||
.Ic new\-window ,
|
||||
.Ic new\-session ,
|
||||
.Ic split\-window ,
|
||||
.Ic respawn\-window ,
|
||||
.Ic respawn\-pane
|
||||
.Ic respawn\-window
|
||||
and
|
||||
.Ic display\-popup
|
||||
.Ic respawn\-pane
|
||||
commands allow
|
||||
.Ar shell\-command
|
||||
to be given as multiple arguments and executed directly (without
|
||||
@@ -5091,9 +5085,6 @@ The available features are:
|
||||
.Bl -tag -width Ds
|
||||
.It 256
|
||||
Supports 256 colours with the SGR escape sequences.
|
||||
.It appesc
|
||||
Supports application escape key mode, where the Escape key sends a sequence
|
||||
that cannot be mistaken for the start of another key.
|
||||
.It clipboard
|
||||
Allows setting the system clipboard.
|
||||
.It ccolour
|
||||
@@ -8842,8 +8833,6 @@ These are set automatically if the
|
||||
capability is present.
|
||||
.It Em \&Dseks , \&Eneks
|
||||
Disable and enable extended keys.
|
||||
.It Em \&Dsesc , \&Enesc
|
||||
Disable and enable application escape key mode.
|
||||
.It Em \&Dsfcs , \&Enfcs
|
||||
Disable and enable focus reporting.
|
||||
These are set automatically if the
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tmux.h,v 1.1447 2026/09/22 14:10:26 nicm Exp $ */
|
||||
/* $OpenBSD: tmux.h,v 1.1442 2026/09/21 10:22:31 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -69,7 +69,6 @@ struct options_array_item;
|
||||
struct options_entry;
|
||||
struct prompt;
|
||||
struct window_pane_prompt;
|
||||
struct redraw_damage;
|
||||
struct redraw_scene;
|
||||
struct redraw_span;
|
||||
struct screen_write_citem;
|
||||
@@ -465,7 +464,6 @@ enum tty_code_code {
|
||||
TTYC_DL1,
|
||||
TTYC_DSBP,
|
||||
TTYC_DSEKS,
|
||||
TTYC_DSESC,
|
||||
TTYC_DSFCS,
|
||||
TTYC_DSMG,
|
||||
TTYC_E3,
|
||||
@@ -476,7 +474,6 @@ enum tty_code_code {
|
||||
TTYC_ENACS,
|
||||
TTYC_ENBP,
|
||||
TTYC_ENEKS,
|
||||
TTYC_ENESC,
|
||||
TTYC_ENFCS,
|
||||
TTYC_ENMG,
|
||||
TTYC_FSL,
|
||||
@@ -1185,7 +1182,6 @@ struct menu {
|
||||
struct menu_item *items;
|
||||
u_int count;
|
||||
u_int width;
|
||||
u_int item_width;
|
||||
};
|
||||
typedef void (*menu_choice_cb)(struct menu *, u_int, key_code, void *);
|
||||
|
||||
@@ -1403,7 +1399,6 @@ struct window_pane {
|
||||
struct screen base;
|
||||
|
||||
struct screen status_screen;
|
||||
u_int status_generation;
|
||||
|
||||
TAILQ_HEAD(, window_mode_entry) modes;
|
||||
|
||||
@@ -1434,7 +1429,6 @@ struct window_pane {
|
||||
TAILQ_HEAD(window_panes, window_pane);
|
||||
TAILQ_HEAD(window_panes_zindex, window_pane);
|
||||
RB_HEAD(window_pane_tree, window_pane);
|
||||
TAILQ_HEAD(redraw_damages, redraw_damage);
|
||||
|
||||
/* Window structure. */
|
||||
struct window {
|
||||
@@ -1478,9 +1472,6 @@ struct window {
|
||||
|
||||
uint64_t redraw_scene_generation;
|
||||
|
||||
struct redraw_damages damage;
|
||||
u_int damage_count;
|
||||
|
||||
struct menu_data *menu;
|
||||
u_int menu_last_px;
|
||||
u_int menu_last_py;
|
||||
@@ -1748,7 +1739,6 @@ struct tty_term {
|
||||
#define TERM_VT100LIKE 0x20
|
||||
#define TERM_SIXEL 0x40
|
||||
#define TERM_INVALIDMS 0x80
|
||||
#define TERM_NOREPLACE 0x100
|
||||
int flags;
|
||||
|
||||
LIST_ENTRY(tty_term) entry;
|
||||
@@ -1806,8 +1796,6 @@ struct tty {
|
||||
struct event timer;
|
||||
size_t discarded;
|
||||
|
||||
size_t sync_offset;
|
||||
|
||||
struct termios tio;
|
||||
|
||||
struct grid_cell cell;
|
||||
@@ -1855,7 +1843,7 @@ struct tty {
|
||||
};
|
||||
|
||||
/* Terminal command context. */
|
||||
typedef void (*tty_ctx_redraw_cb)(const struct tty_ctx *, u_int, u_int);
|
||||
typedef void (*tty_ctx_redraw_cb)(const struct tty_ctx *);
|
||||
typedef int (*tty_ctx_set_client_cb)(struct tty_ctx *, struct client *);
|
||||
struct tty_ctx {
|
||||
struct screen *s;
|
||||
@@ -2996,7 +2984,7 @@ void tty_default_colours(struct grid_cell *, struct window_pane *, u_int *);
|
||||
/* tty-term.c */
|
||||
extern struct tty_terms tty_terms;
|
||||
u_int tty_term_ncodes(void);
|
||||
void tty_term_apply(struct tty_term *, const char *, int, int);
|
||||
void tty_term_apply(struct tty_term *, const char *, int);
|
||||
void tty_term_apply_overrides(struct tty_term *);
|
||||
struct tty_term *tty_term_create(struct tty *, char *, char **, u_int, char **);
|
||||
void tty_term_free(struct tty_term *);
|
||||
@@ -3216,7 +3204,6 @@ void cmdq_print_data(struct cmdq_item *, struct evbuffer *);
|
||||
void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...);
|
||||
|
||||
/* cmd-wait-for.c */
|
||||
void cmd_wait_for_client_lost(struct client *);
|
||||
void cmd_wait_for_flush(void);
|
||||
|
||||
/* client.c */
|
||||
@@ -3642,12 +3629,8 @@ void redraw_screen(struct client *);
|
||||
void redraw_pane(struct client *, struct window_pane *);
|
||||
void redraw_pane_scrollbar(struct client *, struct window_pane *);
|
||||
void redraw_free_scene(struct redraw_scene *);
|
||||
int redraw_client_has_window(struct client *, struct window *);
|
||||
void redraw_invalidate_scene(struct window *);
|
||||
void redraw_invalidate_all_scenes(void);
|
||||
void redraw_damage_window(struct window *, u_int, u_int, u_int, u_int);
|
||||
void redraw_free_damage(struct window *);
|
||||
void redraw_client_damage(struct client *);
|
||||
int redraw_get_status_border_cell_type(struct redraw_span **, u_int);
|
||||
|
||||
/* screen.c */
|
||||
@@ -3821,9 +3804,6 @@ int window_pane_get_pane_status(struct window_pane *);
|
||||
struct style_range *window_pane_status_get_range(struct window_pane *, u_int,
|
||||
u_int);
|
||||
int window_pane_is_floating(struct window_pane *);
|
||||
int window_pane_is_floating_with_hidden(struct window_pane *);
|
||||
void window_redraw_floating_pane(struct window_pane *, int, int,
|
||||
int, int);
|
||||
|
||||
/* window-border.c */
|
||||
void window_set_fill_cells(struct window *);
|
||||
@@ -4163,7 +4143,6 @@ void menu_add_item(struct menu *, const struct menu_item *,
|
||||
struct cmdq_item *, struct client *,
|
||||
struct cmd_find_state *);
|
||||
void menu_free(struct menu *);
|
||||
void menu_get_size(struct menu *, enum box_lines, u_int *, u_int *);
|
||||
int menu_display(struct menu *, int, int, struct cmdq_item *,
|
||||
u_int, u_int, struct client *, enum box_lines, const char *,
|
||||
const char *, const char *, struct cmd_find_state *,
|
||||
|
||||
+2
-7
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tty-draw.c,v 1.17 2026/09/22 06:49:47 nicm Exp $ */
|
||||
/* $OpenBSD: tty-draw.c,v 1.16 2026/09/21 10:22:31 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -218,12 +218,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
|
||||
}
|
||||
|
||||
/* Did the previous line wrap on to this one? */
|
||||
if (py != 0 &&
|
||||
atx == 0 &&
|
||||
tty->cx >= tty->sx &&
|
||||
tty->cy != UINT_MAX &&
|
||||
tty->cy + 1 == aty &&
|
||||
nx == tty->sx) {
|
||||
if (py != 0 && atx == 0 && tty->cx >= tty->sx && nx == tty->sx) {
|
||||
gl = grid_get_line(gd, gd->hsize + py - 1);
|
||||
if (gl->flags & GRID_LINE_WRAPPED)
|
||||
wrapped = 1;
|
||||
|
||||
+5
-21
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tty-features.c,v 1.46 2026/09/24 08:16:13 nicm Exp $ */
|
||||
/* $OpenBSD: tty-features.c,v 1.43 2026/08/31 12:41:03 kirill Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -189,18 +189,6 @@ static const struct tty_feature tty_feature_focus = {
|
||||
0
|
||||
};
|
||||
|
||||
/* Terminal supports application escape key mode. */
|
||||
static const char *const tty_feature_appesc_capabilities[] = {
|
||||
"Enesc=\\E[?7727h",
|
||||
"Dsesc=\\E[?7727l",
|
||||
NULL
|
||||
};
|
||||
static const struct tty_feature tty_feature_appesc = {
|
||||
"appesc",
|
||||
tty_feature_appesc_capabilities,
|
||||
0
|
||||
};
|
||||
|
||||
/* Terminal supports cursor styles. */
|
||||
static const char *const tty_feature_cstyle_capabilities[] = {
|
||||
"Ss=\\E[%p1%d q",
|
||||
@@ -210,7 +198,7 @@ static const char *const tty_feature_cstyle_capabilities[] = {
|
||||
static const struct tty_feature tty_feature_cstyle = {
|
||||
"cstyle",
|
||||
tty_feature_cstyle_capabilities,
|
||||
TERM_NOREPLACE
|
||||
0
|
||||
};
|
||||
|
||||
/* Terminal supports cursor colours. */
|
||||
@@ -390,7 +378,6 @@ static const struct tty_feature tty_feature_utf8 = {
|
||||
/* Available terminal features. */
|
||||
static const struct tty_feature *const tty_features[] = {
|
||||
&tty_feature_256,
|
||||
&tty_feature_appesc,
|
||||
&tty_feature_bpaste,
|
||||
&tty_feature_ccolour,
|
||||
&tty_feature_clipboard,
|
||||
@@ -554,11 +541,11 @@ tty_apply_features(struct tty_term *term)
|
||||
capability = tf->capabilities;
|
||||
while (*capability != NULL) {
|
||||
log_debug("adding capability: %s", *capability);
|
||||
tty_term_apply(term, *capability, 1, tf->flags);
|
||||
tty_term_apply(term, *capability, 1);
|
||||
capability++;
|
||||
}
|
||||
}
|
||||
term->flags |= (tf->flags & ~TERM_NOREPLACE);
|
||||
term->flags |= tf->flags;
|
||||
if (tf == &tty_feature_utf8)
|
||||
c->flags |= CLIENT_UTF8;
|
||||
}
|
||||
@@ -581,7 +568,6 @@ tty_default_features(struct client *c, const char *name, u_int version)
|
||||
"256,RGB,bpaste,clipboard,mouse,strikethrough,title"
|
||||
{ .name = "mintty",
|
||||
.features = TTY_FEATURES_BASE_MODERN_XTERM ","
|
||||
"appesc,"
|
||||
"ccolour,"
|
||||
"cstyle,"
|
||||
"extkeys,"
|
||||
@@ -637,7 +623,6 @@ tty_default_features(struct client *c, const char *name, u_int version)
|
||||
"extkeys,"
|
||||
"focus,"
|
||||
"hyperlinks,"
|
||||
"margins,"
|
||||
"usstyle"
|
||||
},
|
||||
{ .name = "ghostty",
|
||||
@@ -646,7 +631,6 @@ tty_default_features(struct client *c, const char *name, u_int version)
|
||||
"cstyle,"
|
||||
"extkeys,"
|
||||
"focus,"
|
||||
"margins,"
|
||||
"overline,"
|
||||
"hyperlinks,"
|
||||
"osc7,"
|
||||
@@ -677,7 +661,7 @@ tty_default_features(struct client *c, const char *name, u_int version)
|
||||
"cstyle,"
|
||||
"extkeys,"
|
||||
"focus"
|
||||
},
|
||||
}
|
||||
};
|
||||
u_int i;
|
||||
|
||||
|
||||
+3
-9
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tty-term.c,v 1.111 2026/09/22 14:10:26 nicm Exp $ */
|
||||
/* $OpenBSD: tty-term.c,v 1.109 2026/08/25 08:37:08 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -90,7 +90,6 @@ static const struct tty_term_code_entry tty_term_codes[] = {
|
||||
[TTYC_DL1] = { TTYCODE_STRING, "dl1" },
|
||||
[TTYC_DL] = { TTYCODE_STRING, "dl" },
|
||||
[TTYC_DSEKS] = { TTYCODE_STRING, "Dseks" },
|
||||
[TTYC_DSESC] = { TTYCODE_STRING, "Dsesc" },
|
||||
[TTYC_DSFCS] = { TTYCODE_STRING, "Dsfcs" },
|
||||
[TTYC_DSBP] = { TTYCODE_STRING, "Dsbp" },
|
||||
[TTYC_DSMG] = { TTYCODE_STRING, "Dsmg" },
|
||||
@@ -102,7 +101,6 @@ static const struct tty_term_code_entry tty_term_codes[] = {
|
||||
[TTYC_ENACS] = { TTYCODE_STRING, "enacs" },
|
||||
[TTYC_ENBP] = { TTYCODE_STRING, "Enbp" },
|
||||
[TTYC_ENEKS] = { TTYCODE_STRING, "Eneks" },
|
||||
[TTYC_ENESC] = { TTYCODE_STRING, "Enesc" },
|
||||
[TTYC_ENFCS] = { TTYCODE_STRING, "Enfcs" },
|
||||
[TTYC_ENMG] = { TTYCODE_STRING, "Enmg" },
|
||||
[TTYC_FSL] = { TTYCODE_STRING, "fsl" },
|
||||
@@ -364,8 +362,7 @@ tty_term_override_next(const char *s, size_t *offset)
|
||||
}
|
||||
|
||||
void
|
||||
tty_term_apply(struct tty_term *term, const char *capabilities, int quiet,
|
||||
int flags)
|
||||
tty_term_apply(struct tty_term *term, const char *capabilities, int quiet)
|
||||
{
|
||||
const struct tty_term_code_entry *ent;
|
||||
struct tty_code *code;
|
||||
@@ -408,9 +405,6 @@ tty_term_apply(struct tty_term *term, const char *capabilities, int quiet,
|
||||
if (strcmp(s, ent->name) != 0)
|
||||
continue;
|
||||
code = &term->codes[i];
|
||||
if ((flags & TERM_NOREPLACE) &&
|
||||
code->type != TTYCODE_NONE)
|
||||
continue;
|
||||
|
||||
if (remove) {
|
||||
code->type = TTYCODE_NONE;
|
||||
@@ -463,7 +457,7 @@ tty_term_apply_overrides(struct tty_term *term)
|
||||
offset = 0;
|
||||
first = tty_term_override_next(s, &offset);
|
||||
if (first != NULL && fnmatch(first, term->name, 0) == 0)
|
||||
tty_term_apply(term, s + offset, 0, 0);
|
||||
tty_term_apply(term, s + offset, 0);
|
||||
a = options_array_next(a);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tty.c,v 1.482 2026/09/22 06:58:06 nicm Exp $ */
|
||||
/* $OpenBSD: tty.c,v 1.481 2026/09/21 10:22:31 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -502,7 +502,8 @@ tty_stop_tty(struct tty *tty)
|
||||
if (tty_term_has(tty->term, TTYC_DSBP))
|
||||
tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP));
|
||||
|
||||
tty_raw(tty, tty_term_string(tty->term, TTYC_DSESC));
|
||||
if (tty->term->flags & TERM_VT100LIKE)
|
||||
tty_raw(tty, "\033[?7727l");
|
||||
tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS));
|
||||
tty_raw(tty, tty_term_string(tty->term, TTYC_DSEKS));
|
||||
|
||||
@@ -559,7 +560,8 @@ tty_update_features(struct tty *tty)
|
||||
tty_puts(tty, tty_term_string(tty->term, TTYC_ENEKS));
|
||||
if (options_get_number(global_options, "focus-events"))
|
||||
tty_puts(tty, tty_term_string(tty->term, TTYC_ENFCS));
|
||||
tty_puts(tty, tty_term_string(tty->term, TTYC_ENESC));
|
||||
if (tty->term->flags & TERM_VT100LIKE)
|
||||
tty_puts(tty, "\033[?7727h");
|
||||
|
||||
/*
|
||||
* Features might have changed since the first draw during attach. For
|
||||
@@ -1115,7 +1117,7 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx)
|
||||
*/
|
||||
if (tty_large_region(tty, ctx) || ctx->flags & TTY_CTX_PANE_OBSCURED) {
|
||||
log_debug("%s: %s large region redraw", __func__, c->name);
|
||||
ctx->redraw_cb(ctx, ctx->orupper, ctx->orlower - ctx->orupper + 1);
|
||||
ctx->redraw_cb(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1517,7 +1519,6 @@ tty_sync_start(struct tty *tty)
|
||||
if (tty->flags & TTY_SYNCING)
|
||||
return;
|
||||
tty->flags |= TTY_SYNCING;
|
||||
tty->sync_offset = EVBUFFER_LENGTH(tty->out);
|
||||
|
||||
if (tty_term_has(tty->term, TTYC_SYNC)) {
|
||||
log_debug("%s sync start", tty->client->name);
|
||||
@@ -1924,7 +1925,7 @@ tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
|
||||
u_int i, j;
|
||||
|
||||
if (ctx->flags & TTY_CTX_WINDOW_BIGGER) {
|
||||
ctx->redraw_cb(ctx, 0, ctx->sy);
|
||||
ctx->redraw_cb(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1983,7 +1984,7 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
|
||||
tty->cy == tty->rlower)
|
||||
tty_draw_pane(tty, ctx, ctx->ocy);
|
||||
else
|
||||
ctx->redraw_cb(ctx, ctx->ocy, 1);
|
||||
ctx->redraw_cb(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window-copy.c,v 1.431 2026/09/21 10:43:37 nicm Exp $ */
|
||||
/* $OpenBSD: window-copy.c,v 1.430 2026/09/21 10:22:31 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -401,7 +401,7 @@ window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx,
|
||||
|
||||
sy = screen_hsize(src) + screen_size_y(src);
|
||||
if (trim) {
|
||||
while (sy > screen_hsize(src) + 1) {
|
||||
while (sy > screen_hsize(src)) {
|
||||
gl = grid_peek_line(src->grid, sy - 1);
|
||||
if (gl == NULL || gl->cellused != 0)
|
||||
break;
|
||||
@@ -768,7 +768,6 @@ window_copy_scroll(struct window_pane *wp, int sl_mpos, u_int my,
|
||||
struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
|
||||
|
||||
if (wme != NULL) {
|
||||
window_redraw_active_switch(wp->window, wp);
|
||||
window_set_active_pane(wp->window, wp, 0);
|
||||
window_copy_scroll1(wme, wp, sl_mpos, my, tty_oy, scroll_exit);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window.c,v 1.377 2026/09/21 10:33:16 nicm Exp $ */
|
||||
/* $OpenBSD: window.c,v 1.376 2026/09/21 10:22:31 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -415,7 +415,6 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel)
|
||||
TAILQ_INIT(&w->panes);
|
||||
TAILQ_INIT(&w->z_index);
|
||||
TAILQ_INIT(&w->last_panes);
|
||||
TAILQ_INIT(&w->damage);
|
||||
w->active = NULL;
|
||||
|
||||
w->lastlayout = -1;
|
||||
@@ -469,7 +468,6 @@ window_destroy(struct window *w)
|
||||
|
||||
menu_destroy(w);
|
||||
window_destroy_panes(w);
|
||||
redraw_free_damage(w);
|
||||
|
||||
if (event_initialized(&w->name_event))
|
||||
evtimer_del(&w->name_event);
|
||||
@@ -720,7 +718,6 @@ int
|
||||
window_set_active_pane(struct window *w, struct window_pane *wp, int notify)
|
||||
{
|
||||
struct window_pane *lastwp;
|
||||
int unzoomed;
|
||||
|
||||
log_debug("%s: pane %%%u", __func__, wp->id);
|
||||
|
||||
@@ -728,8 +725,7 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify)
|
||||
return (0);
|
||||
if (w->modal != NULL && wp != w->modal)
|
||||
return (0);
|
||||
unzoomed = (w->flags & WINDOW_ZOOMED) && !window_pane_is_visible(wp);
|
||||
if (unzoomed)
|
||||
if ((w->flags & WINDOW_ZOOMED) && !window_pane_is_visible(wp))
|
||||
window_unzoom(w, 1);
|
||||
lastwp = w->active;
|
||||
|
||||
@@ -746,19 +742,7 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify)
|
||||
}
|
||||
|
||||
tty_update_window_offset(w);
|
||||
|
||||
/*
|
||||
* Unzooming changes every pane's geometry and needs a full window
|
||||
* redraw. Otherwise, only the previous and new active pane's border
|
||||
* and status appearance changed, so avoid redrawing unaffected pane
|
||||
* content.
|
||||
*/
|
||||
if (unzoomed)
|
||||
server_redraw_window(w);
|
||||
else {
|
||||
server_redraw_window_borders(w);
|
||||
server_status_window(w);
|
||||
}
|
||||
server_redraw_window(w);
|
||||
|
||||
if (notify)
|
||||
window_fire_pane_changed(w, w->active, lastwp);
|
||||
@@ -2929,52 +2913,3 @@ window_pane_is_floating(struct window_pane *wp)
|
||||
return (0);
|
||||
return (1);
|
||||
}
|
||||
|
||||
int
|
||||
window_pane_is_floating_with_hidden(struct window_pane *wp)
|
||||
{
|
||||
struct layout_cell *lc = wp->layout_cell;
|
||||
|
||||
if (lc == NULL)
|
||||
lc = wp->saved_layout_cell;
|
||||
if (lc == NULL || (lc->flags & LAYOUT_CELL_FLOATING) == 0)
|
||||
return (0);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Report damage for a floating pane, including its border and scrollbar. */
|
||||
static void
|
||||
window_damage_floating_pane(struct window_pane *wp, int xoff, int yoff,
|
||||
int sx, int sy)
|
||||
{
|
||||
struct window *w = wp->window;
|
||||
int x0, x1, y0, y1, sb_left = 0, sb_right = 0;
|
||||
struct style *sb_sy = &wp->scrollbar_style;
|
||||
|
||||
if (window_pane_scrollbar_reserve(wp)) {
|
||||
if (w->sb_pos == PANE_SCROLLBARS_LEFT)
|
||||
sb_left = sb_sy->width + sb_sy->pad;
|
||||
else
|
||||
sb_right = sb_sy->width + sb_sy->pad;
|
||||
}
|
||||
x0 = xoff - 1 - sb_left;
|
||||
x1 = xoff + sx + sb_right;
|
||||
y0 = yoff - 1;
|
||||
y1 = yoff + sy;
|
||||
if (x0 < 0)
|
||||
x0 = 0;
|
||||
if (y0 < 0)
|
||||
y0 = 0;
|
||||
if (x1 >= x0 && y1 >= y0)
|
||||
redraw_damage_window(w, x0, y0, x1 - x0 + 1U, y1 - y0 + 1U);
|
||||
}
|
||||
|
||||
/* Report damage for a floating pane's old and new areas. */
|
||||
void
|
||||
window_redraw_floating_pane(struct window_pane *wp, int oxoff, int oyoff,
|
||||
int osx, int osy)
|
||||
{
|
||||
window_damage_floating_pane(wp, oxoff, oyoff, osx, osy);
|
||||
window_damage_floating_pane(wp, wp->xoff, wp->yoff, wp->sx, wp->sy);
|
||||
server_status_window(wp->window);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user