Merge branch 'master' into floating_panes

This commit is contained in:
Dane Jensen
2026-07-01 14:37:05 -07:00
33 changed files with 688 additions and 398 deletions

75
.github/workflows/regress.yml vendored Normal file
View File

@@ -0,0 +1,75 @@
name: 'Run Tests'
on:
workflow_dispatch:
schedule:
- cron: '33 3 * * *'
permissions:
contents: read
concurrency:
group: tmux-tests
cancel-in-progress: true
jobs:
regress:
name: ${{ matrix.name }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- name: ubuntu-24.04-x64
runner: ubuntu-24.04
make: make
# - name: ubuntu-24.04-arm64
# runner: ubuntu-24.04-arm
# make: make
# - name: macos-26-arm64
# runner: macos-26
# make: gmake
steps:
- name: checkout
uses: actions/checkout@v4
- name: dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y \
autoconf \
automake \
bison \
build-essential \
libevent-dev \
libncurses-dev \
libutf8proc-dev \
pkg-config
- name: dependencies
if: runner.os == 'macOS'
run: |
brew install \
autoconf \
automake \
bison \
libevent \
make \
ncurses \
utf8proc \
pkg-config
- name: build
run: |
sh autogen.sh
./configure --enable-utf8proc
${{ matrix.make }} -j"$(getconf _NPROCESSORS_ONLN)"
- name: test
run: |
cd regress
${{ matrix.make }}

14
CHANGES
View File

@@ -1,3 +1,16 @@
CHANGES FROM 3.7a TO 3.7b
* Fix so that the end of a synchronized update again triggers a redraw.
CHANGES FROM 3.7 TO 3.7a
* Fix crash in break-pane when no name is provided.
* Scrollbar options are now cached rather than being looked up for every redraw
(issue 5298).
* Only forbid #( in names, allow #[, empty names, : and .
CHANGES FROM 3.6b TO 3.7
* Add floating panes. These are panes which sit above the layout ("tiled
@@ -5,6 +18,7 @@ CHANGES FROM 3.6b TO 3.7
the same escape sequence support). Floating panes are created with the
new-pane command, bound to * by default.
This is an early release of this feature and they are relatively limited.
Currently floating panes can only be moved and resized using the mouse. The
default second status line (if status-format is set to 2) has changed to show
a list of panes. Many obvious features are not yet available for floating

View File

@@ -232,7 +232,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
n = 1;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, NULL, NULL, NULL);
format_defaults(ft, tc, NULL, NULL, NULL);
format_add(ft, "notes_only", "%d", notes_only);
format_add(ft, "key_has_repeat", "%d", key_bindings_has_repeat(l, n));
format_add(ft, "key_string_width", "%u", cmd_list_keys_get_width(l, n));

View File

@@ -290,6 +290,58 @@ colour_tostring(int c)
return ("invalid");
}
/* Convert colour to an SGR escape sequence. */
const char *
colour_toescape(struct client *c, int colour, int bg)
{
static char s[32];
u_char r, g, b;
int n, flags = (TERM_256COLOURS|TERM_RGBCOLOURS);
u_int o = (bg ? 40 : 30);
if (c != NULL && (c->tty.flags & TTY_OPENED) && c->tty.term != NULL)
flags = c->tty.term->flags;
if (colour & COLOUR_FLAG_THEME) {
n = colour & 0xff;
if (c != NULL && (u_int)n < COLOUR_THEME_COUNT)
colour = c->theme_colours[n];
else
colour = colour_theme_terminal_colour(n);
}
if (colour == 8 || colour == 9) {
xsnprintf(s, sizeof s, "\033[%dm", o + 9);
return (s);
}
if ((~flags & TERM_RGBCOLOURS) & (colour & COLOUR_FLAG_RGB)) {
colour_split_rgb(colour, &r, &g, &b);
colour = colour_find_rgb(r, g, b);
}
if ((~flags & TERM_256COLOURS) & (colour & COLOUR_FLAG_256))
colour = colour_256to16(colour);
if (colour & COLOUR_FLAG_RGB) {
colour_split_rgb(colour, &r, &g, &b);
xsnprintf(s, sizeof s, "\033[%d;2;%u;%u;%um", o + 8, r, g, b);
return (s);
}
if (colour & COLOUR_FLAG_256) {
xsnprintf(s, sizeof s, "\033[%d;5;%um", o + 8, colour & 0xff);
return (s);
}
if (colour >= 0 && colour <= 7) {
xsnprintf(s, sizeof s, "\033[%dm", colour + o);
return (s);
}
if (colour >= 90 && colour <= 97) {
xsnprintf(s, sizeof s, "\033[%dm", colour + o - 30);
return (s);
}
return (NULL);
}
/* Convert background colour to theme. */
enum client_theme
colour_totheme(int c)

View File

@@ -121,6 +121,8 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
#define FORMAT_CLIENT_TERMCAP 0x1000000
#define FORMAT_CLIENT_TERMFEAT 0x2000000
#define FORMAT_CLIENT_ENVIRON 0x4000000
#define FORMAT_COLOUR_ESC_FG 0x8000000
#define FORMAT_COLOUR_ESC_BG 0x10000000
/* Limit on recursion. */
#define FORMAT_LOOP_LIMIT 100
@@ -4489,7 +4491,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
break;
/* Check single character modifiers with no arguments. */
if (strchr("labcdnwETSWPL!<>", cp[0]) != NULL &&
if (strchr("labdnwETSWPL!<>", cp[0]) != NULL &&
format_is_end(cp[1])) {
format_add_modifier(&list, count, cp, 1, NULL, 0);
cp++;
@@ -4511,7 +4513,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
}
/* Now try single character with arguments. */
if (strchr("ImCLNPSst=pReqW", cp[0]) == NULL)
if (strchr("ImCLNPSst=pReqWc", cp[0]) == NULL)
break;
c = cp[0];
@@ -5289,6 +5291,12 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
break;
case 'c':
modifiers |= FORMAT_COLOUR;
if (fm->argc < 1)
break;
if (strchr(fm->argv[0], 'f') != NULL)
modifiers |= FORMAT_COLOUR_ESC_FG;
if (strchr(fm->argv[0], 'b') != NULL)
modifiers |= FORMAT_COLOUR_ESC_BG;
break;
case 'd':
modifiers |= FORMAT_DIRNAME;
@@ -5487,11 +5495,28 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
/* Is this a colour? */
if (modifiers & FORMAT_COLOUR) {
new = format_expand1(es, copy);
c = colour_fromstring(new);
if (c == -1 || (c = colour_force_rgb(c)) == -1)
value = xstrdup("");
else
xasprintf(&value, "%06x", c & 0xffffff);
if (modifiers & (FORMAT_COLOUR_ESC_FG|FORMAT_COLOUR_ESC_BG)) {
if (strcasecmp(new, "none") == 0)
value = xstrdup("\033[0m");
else if ((c = colour_fromstring(new)) == -1)
value = xstrdup("");
else {
if (modifiers & FORMAT_COLOUR_ESC_BG)
cp = colour_toescape(ft->c, c, 1);
else
cp = colour_toescape(ft->c, c, 0);
if (cp == NULL)
value = xstrdup("");
else
value = xstrdup(cp);
}
} else {
c = colour_fromstring(new);
if (c == -1 || (c = colour_force_rgb(c)) == -1)
value = xstrdup("");
else
xasprintf(&value, "%06x", c & 0xffffff);
}
free(new);
goto done;
}

48
grid.c
View File

@@ -74,11 +74,45 @@ grid_check_lines(struct grid *gd)
}
}
}
void
grid_check_is_clear(struct grid *gd)
{
struct grid_line *gl;
u_int yy, ny;
assert(gd != NULL);
if (gd->sy == 0) {
assert(gd->linedata == NULL);
return;
}
assert(gd->linedata != NULL);
ny = gd->hsize + gd->sy;
for (yy = 0; yy < ny; yy++) {
gl = &gd->linedata[yy];
assert(gl->celldata == NULL);
assert(gl->cellused == 0);
assert(gl->cellsize == 0);
assert(gl->extddata == NULL);
assert(gl->extdsize == 0);
assert(gl->flags == 0);
assert(gl->time == 0);
}
}
#else
static void
grid_check_lines(__unused struct grid *gd)
{
}
void
grid_check_is_clear(__unused struct grid *gd)
{
}
#endif
/* Store cell in entry. */
@@ -341,28 +375,18 @@ grid_create(u_int sx, u_int sy, u_int hlimit)
{
struct grid *gd;
gd = xmalloc(sizeof *gd);
gd = xcalloc(1, sizeof *gd);
gd->sx = sx;
gd->sy = sy;
if (hlimit != 0)
gd->flags = GRID_HISTORY;
else
gd->flags = 0;
gd->hscrolled = 0;
gd->hsize = 0;
gd->hlimit = hlimit;
gd->scroll_added = 0;
gd->scroll_collected = 0;
gd->scroll_generation = 0;
if (gd->sy != 0)
gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
else
gd->linedata = NULL;
grid_check_is_clear(gd);
return (gd);
}

View File

@@ -265,7 +265,7 @@ layout_parse(struct window *w, const char *layout, char **cause)
window_resize(w, tiled_lc->sx, tiled_lc->sy, -1, -1);
/* Destroy the old layout and swap to the new. */
layout_free_cell(w->layout_root);
layout_free_cell(w->layout_root, 0);
w->layout_root = tiled_lc;
/* Assign the panes into the cells. */
@@ -291,7 +291,7 @@ layout_parse(struct window *w, const char *layout, char **cause)
return (0);
fail:
layout_free_cell(tiled_lc);
layout_free_cell(tiled_lc, 0);
return (-1);
}
@@ -423,6 +423,6 @@ layout_construct(struct layout_cell *lcparent, const char **layout,
return (0);
fail:
layout_free_cell(*lc);
layout_free_cell(*lc, 0);
return (-1);
}

View File

@@ -124,12 +124,12 @@ layout_set_previous(struct window *w)
}
static struct window_pane *
layout_first_tiled(struct window *w)
layout_set_first_tiled(struct window *w)
{
struct window_pane *wp;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (!window_pane_is_floating(wp))
if (wp->layout_cell && layout_cell_is_tiled(wp->layout_cell))
return (wp);
}
return (NULL);
@@ -139,19 +139,15 @@ static void
layout_set_even(struct window *w, enum layout_type type)
{
struct window_pane *wp;
struct layout_cell *lc, *lcnew;
struct layout_cell *lcroot, *lcchild;
u_int n, sx, sy;
layout_print_cell(w->layout_root, __func__, 1);
/* Get number of panes. */
n = window_count_panes(w, 0);
if (n <= 1)
return;
/* Free the old root and construct a new. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
if (type == LAYOUT_LEFTRIGHT) {
sx = (n * (PANE_MINIMUM + 1)) - 1;
if (sx < w->sx)
@@ -163,30 +159,30 @@ layout_set_even(struct window *w, enum layout_type type)
sy = w->sy;
sx = w->sx;
}
layout_set_size(lc, sx, sy, 0, 0);
layout_make_node(lc, type);
/* Build new leaf cells. */
layout_free(w, 1);
lcroot = w->layout_root = layout_create_cell(NULL);
layout_set_size(lcroot, sx, sy, 0, 0);
layout_make_node(lcroot, type);
TAILQ_FOREACH(wp, &w->panes, entry) {
if (window_pane_is_floating(wp))
continue;
lcnew = layout_create_cell(lc);
layout_make_leaf(lcnew, wp);
lcnew->sx = w->sx;
lcnew->sy = w->sy;
TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
lcchild = wp->layout_cell;
TAILQ_INSERT_TAIL(&lcroot->cells, lcchild, entry);
lcchild->parent = lcroot;
if (layout_cell_is_tiled(lcchild)) {
lcchild->sx = w->sx;
lcchild->sy = w->sy;
}
}
/* Spread out cells. */
layout_spread_cell(w, lc);
layout_spread_cell(w, lcroot);
/* Fix cell offsets. */
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy, -1, -1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -206,15 +202,14 @@ layout_set_even_v(struct window *w)
static void
layout_set_main_h(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
struct window_pane *wp, *wpmain;
struct layout_cell *lcroot, *lcmain, *lcother, *lcchild;
u_int n, mainh, otherh, sx, sy;
char *cause;
const char *s;
layout_print_cell(w->layout_root, __func__, 1);
/* Get number of panes. */
n = window_count_panes(w, 0);
if (n <= 1)
return;
@@ -255,52 +250,48 @@ layout_set_main_h(struct window *w)
if (sx < w->sx)
sx = w->sx;
/* Free old tree and create a new root. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, sx, mainh + otherh + 1, 0, 0);
layout_make_node(lc, LAYOUT_TOPBOTTOM);
layout_free(w, 1);
lcroot = w->layout_root = layout_create_cell(NULL);
layout_set_size(lcroot, sx, mainh + otherh + 1, 0, 0);
layout_make_node(lcroot, LAYOUT_TOPBOTTOM);
/* Create the main pane. */
lcmain = layout_create_cell(lc);
wpmain = layout_set_first_tiled(w);
lcmain = wpmain->layout_cell;
lcmain->parent = lcroot;
layout_set_size(lcmain, sx, mainh, 0, 0);
layout_make_leaf(lcmain, layout_first_tiled(w));
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
TAILQ_INSERT_TAIL(&lcroot->cells, lcmain, entry);
/* Create the other pane. */
lcother = layout_create_cell(lc);
layout_set_size(lcother, sx, otherh, 0, 0);
if (n == 1) {
wp = TAILQ_NEXT(layout_first_tiled(w), entry);
while (wp != NULL && window_pane_is_floating(wp))
wp = TAILQ_NEXT(wpmain, entry);
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
wp = TAILQ_NEXT(wp, entry);
layout_make_leaf(lcother, wp);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
TAILQ_INSERT_TAIL(&lcroot->cells, wp->layout_cell, entry);
wp->layout_cell->parent = lcroot;
} else {
lcother = layout_create_cell(lcroot);
layout_set_size(lcother, sx, otherh, 0, 0);
layout_make_node(lcother, LAYOUT_LEFTRIGHT);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
TAILQ_INSERT_TAIL(&lcroot->cells, lcother, entry);
/* Add the remaining panes as children. */
TAILQ_FOREACH(wp, &w->panes, entry) {
if (window_pane_is_floating(wp))
if (wp == wpmain)
continue;
if (wp == layout_first_tiled(w))
continue;
lcchild = layout_create_cell(lcother);
layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
layout_make_leaf(lcchild, wp);
lcchild = wp->layout_cell;
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
lcchild->parent = lcother;
if (layout_cell_is_tiled(lcchild))
layout_set_size(lcchild, PANE_MINIMUM, otherh,
0, 0);
}
layout_spread_cell(w, lcother);
}
/* Fix cell offsets. */
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy, -1, -1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -308,15 +299,14 @@ layout_set_main_h(struct window *w)
static void
layout_set_main_h_mirrored(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
struct window_pane *wp, *wpmain;
struct layout_cell *lcroot, *lcmain, *lcother, *lcchild;
u_int n, mainh, otherh, sx, sy;
char *cause;
const char *s;
layout_print_cell(w->layout_root, __func__, 1);
/* Get number of panes. */
n = window_count_panes(w, 0);
if (n <= 1)
return;
@@ -357,52 +347,48 @@ layout_set_main_h_mirrored(struct window *w)
if (sx < w->sx)
sx = w->sx;
/* Free old tree and create a new root. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, sx, mainh + otherh + 1, 0, 0);
layout_make_node(lc, LAYOUT_TOPBOTTOM);
layout_free(w, 1);
lcroot = w->layout_root = layout_create_cell(NULL);
layout_set_size(lcroot, sx, mainh + otherh + 1, 0, 0);
layout_make_node(lcroot, LAYOUT_TOPBOTTOM);
wpmain = layout_set_first_tiled(w);
lcmain = wpmain->layout_cell;
lcmain->parent = lcroot;
layout_set_size(lcmain, sx, mainh, 0, 0);
TAILQ_INSERT_TAIL(&lcroot->cells, lcmain, entry);
/* Create the other pane. */
lcother = layout_create_cell(lc);
layout_set_size(lcother, sx, otherh, 0, 0);
if (n == 1) {
wp = TAILQ_NEXT(layout_first_tiled(w), entry);
while (wp != NULL && window_pane_is_floating(wp))
wp = TAILQ_NEXT(wpmain, entry);
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
wp = TAILQ_NEXT(wp, entry);
layout_make_leaf(lcother, wp);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
TAILQ_INSERT_HEAD(&lcroot->cells, wp->layout_cell, entry);
wp->layout_cell->parent = lcroot;
} else {
lcother = layout_create_cell(lcroot);
layout_set_size(lcother, sx, otherh, 0, 0);
layout_make_node(lcother, LAYOUT_LEFTRIGHT);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
TAILQ_INSERT_HEAD(&lcroot->cells, lcother, entry);
/* Add the remaining panes as children. */
TAILQ_FOREACH(wp, &w->panes, entry) {
if (window_pane_is_floating(wp))
if (wp == wpmain)
continue;
if (wp == layout_first_tiled(w))
continue;
lcchild = layout_create_cell(lcother);
layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
layout_make_leaf(lcchild, wp);
lcchild = wp->layout_cell;
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
lcchild->parent = lcother;
if (layout_cell_is_tiled(lcchild))
layout_set_size(lcchild, PANE_MINIMUM, otherh,
0, 0);
}
layout_spread_cell(w, lcother);
}
/* Create the main pane. */
lcmain = layout_create_cell(lc);
layout_set_size(lcmain, sx, mainh, 0, 0);
layout_make_leaf(lcmain, layout_first_tiled(w));
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
/* Fix cell offsets. */
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy, -1, -1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -410,21 +396,20 @@ layout_set_main_h_mirrored(struct window *w)
static void
layout_set_main_v(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
struct window_pane *wp, *wpmain;
struct layout_cell *lcroot, *lcmain, *lcother, *lcchild;
u_int n, mainw, otherw, sx, sy;
char *cause;
const char *s;
layout_print_cell(w->layout_root, __func__, 1);
/* Get number of panes. */
n = window_count_panes(w, 0);
if (n <= 1)
return;
n--; /* take off main pane */
/* Find available width - take off one line for the border. */
/* Find available width - take off one column for the border. */
sx = w->sx - 1;
/* Get the main pane width. */
@@ -459,52 +444,48 @@ layout_set_main_v(struct window *w)
if (sy < w->sy)
sy = w->sy;
/* Free old tree and create a new root. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
layout_make_node(lc, LAYOUT_LEFTRIGHT);
layout_free(w, 1);
lcroot = w->layout_root = layout_create_cell(NULL);
layout_set_size(lcroot, mainw + otherw + 1, sy, 0, 0);
layout_make_node(lcroot, LAYOUT_LEFTRIGHT);
/* Create the main pane. */
lcmain = layout_create_cell(lc);
wpmain = layout_set_first_tiled(w);
lcmain = wpmain->layout_cell;
lcmain->parent = lcroot;
layout_set_size(lcmain, mainw, sy, 0, 0);
layout_make_leaf(lcmain, layout_first_tiled(w));
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
TAILQ_INSERT_TAIL(&lcroot->cells, lcmain, entry);
/* Create the other pane. */
lcother = layout_create_cell(lc);
layout_set_size(lcother, otherw, sy, 0, 0);
if (n == 1) {
wp = TAILQ_NEXT(layout_first_tiled(w), entry);
while (wp != NULL && window_pane_is_floating(wp))
wp = TAILQ_NEXT(wpmain, entry);
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
wp = TAILQ_NEXT(wp, entry);
layout_make_leaf(lcother, wp);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
TAILQ_INSERT_TAIL(&lcroot->cells, wp->layout_cell, entry);
wp->layout_cell->parent = lcroot;
} else {
lcother = layout_create_cell(lcroot);
layout_make_node(lcother, LAYOUT_TOPBOTTOM);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
layout_set_size(lcother, otherw, sy, 0, 0);
TAILQ_INSERT_TAIL(&lcroot->cells, lcother, entry);
/* Add the remaining panes as children. */
TAILQ_FOREACH(wp, &w->panes, entry) {
if (window_pane_is_floating(wp))
if (wp == wpmain)
continue;
if (wp == layout_first_tiled(w))
continue;
lcchild = layout_create_cell(lcother);
layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
layout_make_leaf(lcchild, wp);
lcchild = wp->layout_cell;
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
lcchild->parent = lcother;
if (layout_cell_is_tiled(lcchild))
layout_set_size(lcchild, otherw, PANE_MINIMUM,
0, 0);
}
layout_spread_cell(w, lcother);
}
/* Fix cell offsets. */
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy, -1, -1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -512,8 +493,8 @@ layout_set_main_v(struct window *w)
static void
layout_set_main_v_mirrored(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
struct window_pane *wp, *wpmain;
struct layout_cell *lcroot, *lcmain, *lcother, *lcchild;
u_int n, mainw, otherw, sx, sy;
char *cause;
const char *s;
@@ -526,7 +507,7 @@ layout_set_main_v_mirrored(struct window *w)
return;
n--; /* take off main pane */
/* Find available width - take off one line for the border. */
/* Find available width - take off one column for the border. */
sx = w->sx - 1;
/* Get the main pane width. */
@@ -561,62 +542,58 @@ layout_set_main_v_mirrored(struct window *w)
if (sy < w->sy)
sy = w->sy;
/* Free old tree and create a new root. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
layout_make_node(lc, LAYOUT_LEFTRIGHT);
layout_free(w, 1);
lcroot = w->layout_root = layout_create_cell(NULL);
layout_set_size(lcroot, mainw + otherw + 1, sy, 0, 0);
layout_make_node(lcroot, LAYOUT_LEFTRIGHT);
wpmain = layout_set_first_tiled(w);
lcmain = wpmain->layout_cell;
lcmain->parent = lcroot;
layout_set_size(lcmain, mainw, sy, 0, 0);
TAILQ_INSERT_TAIL(&lcroot->cells, lcmain, entry);
/* Create the other pane. */
lcother = layout_create_cell(lc);
layout_set_size(lcother, otherw, sy, 0, 0);
if (n == 1) {
wp = TAILQ_NEXT(layout_first_tiled(w), entry);
while (wp != NULL && window_pane_is_floating(wp))
wp = TAILQ_NEXT(wpmain, entry);
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
wp = TAILQ_NEXT(wp, entry);
layout_make_leaf(lcother, wp);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
TAILQ_INSERT_HEAD(&lcroot->cells, wp->layout_cell, entry);
wp->layout_cell->parent = lcroot;
} else {
lcother = layout_create_cell(lcroot);
layout_make_node(lcother, LAYOUT_TOPBOTTOM);
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
layout_set_size(lcother, otherw, sy, 0, 0);
TAILQ_INSERT_HEAD(&lcroot->cells, lcother, entry);
/* Add the remaining panes as children. */
TAILQ_FOREACH(wp, &w->panes, entry) {
if (window_pane_is_floating(wp))
if (wp == wpmain)
continue;
if (wp == layout_first_tiled(w))
continue;
lcchild = layout_create_cell(lcother);
layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
layout_make_leaf(lcchild, wp);
lcchild = wp->layout_cell;
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
lcchild->parent = lcother;
if (layout_cell_is_tiled(lcchild))
layout_set_size(lcchild, otherw, PANE_MINIMUM,
0, 0);
}
layout_spread_cell(w, lcother);
}
/* Create the main pane. */
lcmain = layout_create_cell(lc);
layout_set_size(lcmain, mainw, sy, 0, 0);
layout_make_leaf(lcmain, layout_first_tiled(w));
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
/* Fix cell offsets. */
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy, -1, -1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
void
static void
layout_set_tiled(struct window *w)
{
struct options *oo = w->options;
struct window_pane *wp;
struct layout_cell *lc, *lcrow, *lcchild;
struct layout_cell *lcroot, *lcrow, *lcchild;
u_int n, width, height, used, sx, sy;
u_int i, j, columns, rows, max_columns;
@@ -647,56 +624,59 @@ layout_set_tiled(struct window *w)
if (height < PANE_MINIMUM)
height = PANE_MINIMUM;
/* Free old tree and create a new root. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
sx = ((width + 1) * columns) - 1;
if (sx < w->sx)
sx = w->sx;
sy = ((height + 1) * rows) - 1;
if (sy < w->sy)
sy = w->sy;
layout_set_size(lc, sx, sy, 0, 0);
layout_make_node(lc, LAYOUT_TOPBOTTOM);
/* Create a grid of the cells, skipping any floating panes. */
layout_free(w, 1);
lcroot = w->layout_root = layout_create_cell(NULL);
layout_set_size(lcroot, sx, sy, 0, 0);
layout_make_node(lcroot, LAYOUT_TOPBOTTOM);
/* Create a grid of the tiled cells. */
wp = TAILQ_FIRST(&w->panes);
while (wp != NULL && window_pane_is_floating(wp))
wp = TAILQ_NEXT(wp, entry);
for (j = 0; j < rows; j++) {
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
wp = TAILQ_NEXT(wp, entry);
/* If this is the last cell, all done. */
if (wp == NULL)
break;
/* Create the new row. */
lcrow = layout_create_cell(lc);
layout_set_size(lcrow, w->sx, height, 0, 0);
TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
lcchild = wp->layout_cell;
/* If only one column, just use the row directly. */
if (n - (j * columns) == 1 || columns == 1) {
layout_make_leaf(lcrow, wp);
lcchild->parent = lcroot;
TAILQ_INSERT_TAIL(&lcroot->cells, lcchild, entry);
layout_set_size(lcchild, w->sx, height, 0, 0);
wp = TAILQ_NEXT(wp, entry);
while (wp != NULL && window_pane_is_floating(wp))
wp = TAILQ_NEXT(wp, entry);
continue;
}
/* Add in the columns. */
/* Create the new row. */
lcrow = layout_create_cell(lcroot);
layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
layout_set_size(lcrow, w->sx, height, 0, 0);
TAILQ_INSERT_TAIL(&lcroot->cells, lcrow, entry);
/* Add in the columns. */
for (i = 0; i < columns; i++) {
/* Create and add a pane cell. */
lcchild = layout_create_cell(lcrow);
layout_set_size(lcchild, width, height, 0, 0);
layout_make_leaf(lcchild, wp);
lcchild->parent = lcrow;
TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
layout_set_size(lcchild, width, height, 0, 0);
/* Move to the next non-floating cell. */
wp = TAILQ_NEXT(wp, entry);
while (wp != NULL && window_pane_is_floating(wp))
while (wp != NULL &&
!layout_cell_is_tiled(wp->layout_cell))
wp = TAILQ_NEXT(wp, entry);
if (wp == NULL)
break;
lcchild = wp->layout_cell;
}
/*
@@ -713,21 +693,19 @@ layout_set_tiled(struct window *w)
w->sx - used);
}
/* Adjust the last row height to fit if necessary. */
used = (rows * height) + rows - 1;
if (w->sy > used) {
lcrow = TAILQ_LAST(&lc->cells, layout_cells);
lcrow = TAILQ_LAST(&lcroot->cells, layout_cells);
layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM,
w->sy - used);
}
/* Fix cell offsets. */
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy, -1, -1);
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}

View File

@@ -89,20 +89,24 @@ layout_create_cell(struct layout_cell *lcparent)
/* Free a layout cell. */
void
layout_free_cell(struct layout_cell *lc)
layout_free_cell(struct layout_cell *lc, int only_nodes)
{
struct layout_cell *lcchild;
struct layout_cell *lcchild, *lcnext;
if (lc == NULL)
if (lc == NULL || (only_nodes && lc->type == LAYOUT_WINDOWPANE))
return;
switch (lc->type) {
case LAYOUT_LEFTRIGHT:
case LAYOUT_TOPBOTTOM:
while (!TAILQ_EMPTY(&lc->cells)) {
lcchild = TAILQ_FIRST(&lc->cells);
TAILQ_REMOVE(&lc->cells, lcchild, entry);
layout_free_cell(lcchild);
lcchild = TAILQ_FIRST(&lc->cells);
while (lcchild != NULL) {
lcnext = TAILQ_NEXT(lcchild, entry);
if (!only_nodes || lcchild->type != LAYOUT_WINDOWPANE) {
TAILQ_REMOVE(&lc->cells, lcchild, entry);
layout_free_cell(lcchild, only_nodes);
}
lcchild = lcnext;
}
break;
case LAYOUT_WINDOWPANE:
@@ -715,13 +719,13 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc,
if (lcparent == NULL) {
if (lc->wp != NULL)
*lcroot = NULL;
layout_free_cell(lc);
layout_free_cell(lc, 0);
return;
}
if (!layout_cell_is_tiled(lc)) {
TAILQ_REMOVE(&lcparent->cells, lc, entry);
layout_free_cell(lc);
layout_free_cell(lc, 0);
goto out;
}
@@ -737,7 +741,7 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc,
/* Remove this from the parent's list. */
TAILQ_REMOVE(&lcparent->cells, lc, entry);
layout_free_cell(lc);
layout_free_cell(lc, 0);
out:
/*
@@ -759,7 +763,7 @@ out:
} else
TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry);
layout_free_cell(lcparent);
layout_free_cell(lcparent, 0);
}
}
@@ -777,9 +781,9 @@ layout_init(struct window *w, struct window_pane *wp)
/* Free layout for pane. */
void
layout_free(struct window *w)
layout_free(struct window *w, int only_nodes)
{
layout_free_cell(w->layout_root);
layout_free_cell(w->layout_root, only_nodes);
}
/* Resize the entire layout after window resize. */
@@ -1524,7 +1528,8 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
number = 0;
TAILQ_FOREACH (lc, &parent->cells, entry)
number++;
if (layout_cell_is_tiled(lc))
number++;
if (number <= 1)
return (0);
status = window_get_pane_status(w);
@@ -1552,6 +1557,8 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
changed = 0;
TAILQ_FOREACH (lc, &parent->cells, entry) {
if (!layout_cell_is_tiled(lc))
continue;
change = 0;
if (parent->type == LAYOUT_LEFTRIGHT) {
change = each - (int)lc->sx;

View File

@@ -206,8 +206,6 @@ static const char* mode_tree_help_start[] = {
"#[fg=themelightgrey]"
" C-s #[#{E:tree-mode-border-style},acs]x#[default] Search forward",
"#[fg=themelightgrey]"
" C-r #[#{E:tree-mode-border-style},acs]x#[default] Search backward",
"#[fg=themelightgrey]"
" n #[#{E:tree-mode-border-style},acs]x#[default] Repeat search forward",
"#[fg=themelightgrey]"
" N #[#{E:tree-mode-border-style},acs]x#[default] Repeat search backward",

Binary file not shown.

View File

@@ -35,68 +35,59 @@ $TMUX set-option -qg allow-set-title on || exit 1
$TMUX set-option -qg allow-rename on || exit 1
$TMUX set-option -qg automatic-rename off || exit 1
# Commands reject ':' and '.' for sessions and windows, but allow '#'.
$TMUX rename-session 'session#ok' || fail "session name with # rejected"
must_equal "$($TMUX display-message -p '#{session_name}')" 'session#ok'
must_fail $TMUX rename-session 'session:bad'
must_fail $TMUX rename-session 'session.bad'
# Commands allow empty names, ':', '.', '#' and '#('.
$TMUX rename-session '' || fail "empty session name rejected"
must_equal "$($TMUX display-message -p '#{session_name}')" ''
$TMUX rename-session 'session:.##(ok)' || \
fail "session name with : . or #( rejected"
must_equal "$($TMUX display-message -p '#{session_name}')" 'session:.#(ok)'
$TMUX rename-window 'window#ok' || fail "window name with # rejected"
must_equal "$($TMUX display-message -p '#{window_name}')" 'window#ok'
must_fail $TMUX rename-window 'window:bad'
must_fail $TMUX rename-window 'window.bad'
$TMUX rename-window '' || fail "empty window name rejected"
must_equal "$($TMUX display-message -p '#{window_name}')" ''
$TMUX rename-window 'window:.##(ok)' || \
fail "window name with : . or #( rejected"
must_equal "$($TMUX display-message -p '#{window_name}')" 'window:.#(ok)'
$TMUX set-option -q @name 'format#ok' || exit 1
$TMUX set-option -q @name 'format:.#(ok)' || exit 1
$TMUX rename-session '#{@name}' || fail "format in session name not expanded"
must_equal "$($TMUX display-message -p '#{session_name}')" 'format#ok'
must_equal "$($TMUX display-message -p '#{session_name}')" 'format:.#(ok)'
$TMUX rename-window '#{@name}' || fail "format in window name not expanded"
must_equal "$($TMUX display-message -p '#{window_name}')" 'format#ok'
must_fail $TMUX rename-session '#{session_name}:bad'
must_fail $TMUX rename-window '#{window_name}.bad'
must_equal "$($TMUX display-message -p '#{window_name}')" 'format:.#(ok)'
$TMUX set-option -q @name 'format:.#(ok)' || exit 1
pid=$($TMUX display-message -p '#{pid}')
created=$($TMUX new-session -dP -F '#{session_id}:#{window_id}' \
-s 'new-session#ok' -n 'new-window#ok') || \
fail "new-session name with # rejected"
-s 'new-session:.##(ok)' -n 'new-window:.##(ok)') || \
fail "new-session name with : . or #( rejected"
created_session=${created%:*}
created_window=${created#*:}
must_equal "$($TMUX display-message -pt "$created_session" '#{session_name}')" \
'new-session#ok'
'new-session:.#(ok)'
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
'new-window#ok'
'new-window:.#(ok)'
$TMUX kill-session -t "$created_session"
must_fail $TMUX new-session -d -s 'new-session:bad'
must_fail $TMUX new-session -d -s 'new-session.bad'
must_fail $TMUX new-session -d -n 'new-window:bad'
must_fail $TMUX new-session -d -n 'new-window.bad'
created_window=$($TMUX new-window -dP -F '#{window_id}' \
-n 'created-window#ok') || \
fail "new-window name with # rejected"
-n 'created-window:.##(ok)') || \
fail "new-window name with : . or #( rejected"
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
'created-window#ok'
must_fail $TMUX new-window -d -n 'created-window:bad'
must_fail $TMUX new-window -d -n 'created-window.bad'
'created-window:.#(ok)'
created=$($TMUX new-session -dP -F '#{session_id}:#{window_id}' \
-s 'new-session-#{pid}' -n 'new-window-#{pid}') || \
-s 'new-session-#{pid}:.##(ok)' -n 'new-window-#{pid}:.##(ok)') || \
fail "format in new-session name not expanded"
created_session=${created%:*}
created_window=${created#*:}
must_equal "$($TMUX display-message -pt "$created_session" '#{session_name}')" \
"new-session-$pid"
"new-session-$pid:.#(ok)"
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
"new-window-$pid"
"new-window-$pid:.#(ok)"
$TMUX kill-session -t "$created_session"
created_window=$($TMUX new-window -dP -F '#{window_id}' -n '#{@name}') || \
fail "format in new-window name not expanded"
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
'format#ok'
must_fail $TMUX new-session -d -s 'new-session-#{pid}:bad'
must_fail $TMUX new-session -d -n 'new-window-#{pid}.bad'
must_fail $TMUX new-window -d -n '#{window_name}:bad'
'format:.#(ok)'
# Invalid UTF-8 is never allowed for command names.
invalid=$(printf '\302')
@@ -124,9 +115,9 @@ must_equal "$($TMUX list-buffers -F '#{buffer_name}')" 'buffer#:.ok'
# Window names from escape sequences allow '#' except in '#('.
$TMUX send-keys "printf '\\033kescape#:.ok\\033\\\\'" Enter || exit 1
sleep 1
must_equal "$($TMUX display-message -p '#{window_name}')" 'escape#__ok'
must_equal "$($TMUX display-message -p '#{window_name}')" 'escape#:.ok'
# Titles from escape sequences reject only '#'.
# Titles from escape sequences allow '#' except in '#('.
$TMUX send-keys "printf '\\033]2;escape#:.ok\\007'" Enter || exit 1
sleep 1
must_equal "$($TMUX display-message -p '#{pane_title}')" 'escape#:.ok'

View File

@@ -131,11 +131,14 @@ $IN send-keys -l "Z" || exit 1
settle
search_is "hello Z" "C-w did not kill a word"
# C-a then C-k kills the whole line.
# C-a then C-k kills the whole line. The mode prompt no longer fills the rest
# of the row, so insert a marker to distinguish prompt input from tree content
# that may remain visible after the prompt.
$IN send-keys C-a || exit 1
$IN send-keys C-k || exit 1
$IN send-keys -l "X" || exit 1
settle
search_row | grep -q '(search) [^ ]' && fail "C-a C-k did not clear the line"
search_is "X" "C-a C-k did not clear the line"
# --- 3. Editing kept the prompt open the whole time. ---
in_tree_mode || fail "editing keys closed the mode"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -90,7 +90,6 @@ You should see the Greek word 'kosme': "κόσμε"
2.3.2 U-0000E000 = ee 80 80 = "" |
2.3.3 U-0000FFFD = ef bf bd = "<22>" |
2.3.4 U-0010FFFF = f4 8f bf bf = "􏿿" |
2.3.5 U-00110000 = f4 90 80 80 = "<22>" |
|
3 Malformed sequences |
|

View File

@@ -1655,12 +1655,14 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
if (wp != NULL) {
if (wp->base.mode & MODE_SYNC)
screen_write_stop_sync(wp);
screen_write_clear_dirty(wp);
} else {
TAILQ_FOREACH(loop, &scene->w->panes, entry) {
if (!window_pane_is_visible(loop))
continue;
if (loop->base.mode & MODE_SYNC)
screen_write_stop_sync(loop);
screen_write_clear_dirty(loop);
}
}
}

View File

@@ -38,6 +38,7 @@ static int screen_write_overwrite(struct screen_write_ctx *,
struct grid_cell *, u_int);
static int screen_write_combine(struct screen_write_ctx *,
const struct grid_cell *);
static void screen_write_flush_dirty(struct window_pane *);
struct screen_write_citem {
u_int x;
@@ -217,6 +218,48 @@ screen_write_pane_is_obscured(struct screen_write_ctx *ctx)
return (0);
}
/* Should we draw to the TTY? */
static int
screen_write_should_draw_lines(struct screen_write_ctx *ctx, u_int y, u_int ny)
{
struct window_pane *wp = ctx->wp;
struct screen *s = ctx->s;
u_int sy = screen_size_y(s);
bitstr_t *bs;
if (wp != NULL && (wp->flags & (PANE_REDRAW|PANE_DROP)))
return (0);
if (s->mode & MODE_SYNC) {
if (wp != NULL && y < sy && ny != 0) {
bs = wp->sync_dirty;
if (ny > sy - y)
ny = sy - y;
if (bs == NULL || wp->sync_dirty_size != sy) {
if (bs != NULL && wp->sync_dirty_size != sy) {
y = 0;
ny = sy;
}
free(bs);
bs = wp->sync_dirty = bit_alloc(sy);
if (bs == NULL)
fatal("bit_alloc failed");
wp->sync_dirty_size = sy;
}
bit_nset(bs, y, y + ny - 1);
}
return (0);
}
return (1);
}
/* Should we draw this line to the TTY? */
static int
screen_write_should_draw_line(struct screen_write_ctx *ctx, u_int y)
{
return (screen_write_should_draw_lines(ctx, y, 1));
}
/* Set up context for TTY command. */
static void
screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx,
@@ -982,7 +1025,7 @@ screen_write_sync_callback(__unused int fd, __unused short events, void *arg)
if (wp->base.mode & MODE_SYNC) {
wp->base.mode &= ~MODE_SYNC;
wp->flags |= PANE_REDRAW;
screen_write_flush_dirty(wp);
}
}
@@ -1007,13 +1050,15 @@ screen_write_start_sync(struct window_pane *wp)
void
screen_write_stop_sync(struct window_pane *wp)
{
if (wp == NULL)
if (wp == NULL || (~wp->base.mode & MODE_SYNC))
return;
if (event_initialized(&wp->sync_timer))
evtimer_del(&wp->sync_timer);
wp->base.mode &= ~MODE_SYNC;
screen_write_flush_dirty(wp);
log_debug("%s: %%%u stopped sync mode", __func__, wp->id);
}
@@ -1167,9 +1212,6 @@ screen_write_redraw_line(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx,
struct visible_ranges *r;
struct visible_range *ri;
if (s->mode & MODE_SYNC)
return;
r = window_visible_ranges(wp, xoff, yoff + yy, sx, NULL);
for (i = 0; i < r->used; i++) {
ri = &r->ranges[i];
@@ -1208,6 +1250,44 @@ screen_write_redraw_line(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx,
}
}
/* Redraw dirty lines. */
static void
screen_write_flush_dirty(struct window_pane *wp)
{
struct screen_write_ctx ctx;
struct tty_ctx ttyctx;
struct screen *s = &wp->base;
u_int y, sy = screen_size_y(s), lines = 0;
if (wp->sync_dirty == NULL)
return;
screen_write_start_pane(&ctx, wp, s);
screen_write_initctx(&ctx, &ttyctx, 1, 1);
for (y = 0; y < sy; y++) {
if (bit_test(wp->sync_dirty, y)) {
screen_write_redraw_line(&ctx, &ttyctx, y);
lines++;
}
}
log_debug("%s: %%%u had %u dirty lines", __func__, wp->id, lines);
screen_write_stop(&ctx);
screen_write_clear_dirty(wp);
}
/* Clear any dirty lines. */
void
screen_write_clear_dirty(struct window_pane *wp)
{
if (wp != NULL && wp->sync_dirty != NULL) {
free(wp->sync_dirty);
wp->sync_dirty = NULL;
wp->sync_dirty_size = 0;
}
}
/* Redraw all visible cells in a pane. */
static void
screen_write_redraw_pane(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
@@ -1250,7 +1330,7 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
screen_write_initctx(ctx, &ttyctx, 1, 1);
if (s->mode & MODE_SYNC)
if (!screen_write_should_draw_lines(ctx, 0, screen_size_y(s)))
return;
if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
tty_write(tty_cmd_alignmenttest, &ttyctx);
@@ -1291,7 +1371,7 @@ screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
screen_write_collect_flush(ctx, 0, __func__);
ttyctx.n = nx;
if (s->mode & MODE_SYNC)
if (!screen_write_should_draw_line(ctx, s->cy))
return;
if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
tty_write(tty_cmd_insertcharacter, &ttyctx);
@@ -1332,7 +1412,7 @@ screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
screen_write_collect_flush(ctx, 0, __func__);
ttyctx.n = nx;
if (s->mode & MODE_SYNC)
if (!screen_write_should_draw_line(ctx, s->cy))
return;
if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
tty_write(tty_cmd_deletecharacter, &ttyctx);
@@ -1373,7 +1453,7 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
screen_write_collect_flush(ctx, 0, __func__);
ttyctx.n = nx;
if (s->mode & MODE_SYNC)
if (!screen_write_should_draw_line(ctx, s->cy))
return;
if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
tty_write(tty_cmd_clearcharacter, &ttyctx);
@@ -1414,7 +1494,7 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
screen_write_collect_flush(ctx, 0, __func__);
ttyctx.n = ny;
if (s->mode & MODE_SYNC)
if (!screen_write_should_draw_lines(ctx, s->cy, sy - s->cy))
return;
if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
tty_write(tty_cmd_insertline, &ttyctx);
@@ -1441,7 +1521,7 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
screen_write_collect_flush(ctx, 0, __func__);
ttyctx.n = ny;
if (s->mode & MODE_SYNC)
if (!screen_write_should_draw_lines(ctx, s->cy, s->rlower + 1 - s->cy))
return;
if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
tty_write(tty_cmd_insertline, &ttyctx);
@@ -1458,7 +1538,7 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
struct screen *s = ctx->s;
struct grid *gd = s->grid;
struct tty_ctx ttyctx;
u_int sy = screen_size_y(s);
u_int sy = screen_size_y(s), ry;
if (ny == 0)
ny = 1;
@@ -1482,7 +1562,8 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
screen_write_collect_flush(ctx, 0, __func__);
ttyctx.n = ny;
if (s->mode & MODE_SYNC)
ry = s->rlower + 1 - s->rupper;
if (!screen_write_should_draw_lines(ctx, s->rupper, ry))
return;
if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
tty_write(tty_cmd_deleteline, &ttyctx);
@@ -1493,8 +1574,9 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
return;
}
if (ny > s->rlower + 1 - s->cy)
ny = s->rlower + 1 - s->cy;
ry = s->rlower + 1 - s->cy;
if (ny > ry)
ny = ry;
if (ny == 0)
return;
@@ -1509,7 +1591,7 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
screen_write_collect_flush(ctx, 0, __func__);
ttyctx.n = ny;
if (s->mode & MODE_SYNC)
if (!screen_write_should_draw_lines(ctx, s->cy, ry))
return;
if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
tty_write(tty_cmd_deleteline, &ttyctx);
@@ -1643,29 +1725,34 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
{
struct screen *s = ctx->s;
struct tty_ctx ttyctx;
u_int ry;
if (s->cy != s->rupper) {
if (s->cy > 0)
screen_write_set_cursor(ctx, -1, s->cy - 1);
return;
}
if (s->cy == s->rupper) {
#ifdef ENABLE_SIXEL
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
screen_write_collect_flush(ctx, 0, __func__);
grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
screen_write_collect_flush(ctx, 0, __func__);
screen_write_initctx(ctx, &ttyctx, 1, 1);
ttyctx.bg = bg;
screen_write_initctx(ctx, &ttyctx, 1, 1);
ttyctx.bg = bg;
if (s->mode & MODE_SYNC)
return;
if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
tty_write(tty_cmd_reverseindex, &ttyctx);
return;
}
ry = s->rlower + 1 - s->rupper;
if (!screen_write_should_draw_lines(ctx, s->rupper, ry))
return;
if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
tty_write(tty_cmd_reverseindex, &ttyctx);
return;
}
screen_write_redraw_pane(ctx, &ttyctx);
} else if (s->cy > 0)
screen_write_set_cursor(ctx, -1, s->cy - 1);
screen_write_redraw_pane(ctx, &ttyctx);
}
/* Set scroll region. */
@@ -1715,20 +1802,24 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
ctx->bg = bg;
}
if (s->cy == s->rlower) {
if (s->cy != s->rlower) {
if (s->cy < screen_size_y(s) - 1)
screen_write_set_cursor(ctx, -1, s->cy + 1);
return;
}
#ifdef ENABLE_SIXEL
if (rlower == screen_size_y(s) - 1)
redraw = image_scroll_up(s, 1);
else
redraw = image_check_line(s, rupper, rlower - rupper);
if (redraw && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
if (rlower == screen_size_y(s) - 1)
redraw = image_scroll_up(s, 1);
else
redraw = image_check_line(s, rupper, rlower - rupper);
if (redraw && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
screen_write_collect_scroll(ctx, bg);
ctx->scrolled++;
} else if (s->cy < screen_size_y(s) - 1)
screen_write_set_cursor(ctx, -1, s->cy + 1);
grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
screen_write_collect_scroll(ctx, bg);
ctx->scrolled++;
}
/* Scroll up. */
@@ -1768,7 +1859,7 @@ screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
struct screen *s = ctx->s;
struct grid *gd = s->grid;
struct tty_ctx ttyctx;
u_int i;
u_int i, ry;
screen_write_initctx(ctx, &ttyctx, 1, 1);
ttyctx.bg = bg;
@@ -1789,7 +1880,8 @@ screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
screen_write_collect_flush(ctx, 0, __func__);
ttyctx.n = lines;
if (s->mode & MODE_SYNC)
ry = s->rlower + 1 - s->rupper;
if (!screen_write_should_draw_lines(ctx, s->rupper, ry))
return;
if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
tty_write(tty_cmd_scrolldown, &ttyctx);
@@ -1842,7 +1934,7 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1));
screen_write_collect_flush(ctx, 0, __func__);
if (s->mode & MODE_SYNC)
if (!screen_write_should_draw_lines(ctx, s->cy, sy - s->cy))
return;
if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED) {
tty_write(tty_cmd_clearendofscreen, &ttyctx);
@@ -1917,7 +2009,7 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
screen_write_collect_clear(ctx, 0, s->cy);
screen_write_collect_flush(ctx, 0, __func__);
if (s->mode & MODE_SYNC)
if (!screen_write_should_draw_lines(ctx, 0, s->cy + 1))
return;
if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED) {
tty_write(tty_cmd_clearstartofscreen, &ttyctx);
@@ -1990,7 +2082,7 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
screen_write_collect_clear(ctx, 0, sy);
if (s->mode & MODE_SYNC)
if (!screen_write_should_draw_lines(ctx, s->cy, sy - s->cy))
return;
if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED) {
tty_write(tty_cmd_clearscreen, &ttyctx);
@@ -2307,12 +2399,21 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
const char *from)
{
struct screen *s = ctx->s;
struct window_pane *wp = ctx->wp;
u_int y, cx, cy, items = 0;
struct screen_write_citem *ci, *tmp;
struct screen_write_cline *cl;
if (s->mode & MODE_SYNC)
if (wp != NULL && (wp->flags & (PANE_REDRAW|PANE_DROP)))
goto discard;
if (s->mode & MODE_SYNC) {
for (y = 0; y < screen_size_y(s); y++) {
cl = &s->write_list[y];
if (!TAILQ_EMPTY(&cl->items))
screen_write_should_draw_line(ctx, y);
}
goto discard;
}
if (ctx->scrolled != 0) {
if (!screen_write_collect_flush_scrolled(ctx))
@@ -2650,12 +2751,12 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
if (s->mode & MODE_INSERT) {
screen_write_collect_flush(ctx, 0, __func__);
ttyctx.n = width;
if (~s->mode & MODE_SYNC)
if (screen_write_should_draw_line(ctx, s->cy))
tty_write(tty_cmd_insertcharacter, &ttyctx);
}
/* If not writing, done now. */
if (skip || s->mode & MODE_SYNC)
if (skip || !screen_write_should_draw_line(ctx, s->cy))
return;
/* Do a full line redraw if needed. */
@@ -2675,7 +2776,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
for (i = 0, vis = 0; i < r->used; i++)
vis += r->ranges[i].nx;
if (vis >= width) {
if (~s->mode & MODE_SYNC)
if (screen_write_should_draw_line(ctx, s->cy))
tty_write(tty_cmd_cell, &ttyctx);
return;
}
@@ -2685,7 +2786,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
* spaces in the visible regions.
*/
utf8_set(&tmp_gc.data, ' ');
if (s->mode & MODE_SYNC)
if (!screen_write_should_draw_line(ctx, s->cy))
return;
for (i = 0; i < r->used; i++) {
ri = &r->ranges[i];
@@ -2828,7 +2929,7 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
ttyctx.cell = &last;
if (force_wide)
ttyctx.flags |= TTY_CTX_CELL_INVALIDATE;
if (~s->mode & MODE_SYNC)
if (screen_write_should_draw_line(ctx, cy))
tty_write(tty_cmd_cell, &ttyctx);
screen_write_set_cursor(ctx, cx, cy);

View File

@@ -123,7 +123,7 @@ screen_reinit(struct screen *s)
s->saved_cy = UINT_MAX;
screen_reset_tabs(s);
grid_check_is_clear(s->grid);
grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy, 8);
screen_clear_selection(s);

View File

@@ -2048,19 +2048,18 @@ server_client_reset_state(struct client *c)
if (!window_position_is_visible(r, cx))
cursor = 0;
if (window_pane_scrollbar_overlay_visible(wp)) {
sb_w = wp->scrollbar_style.width;
if (sb_w > wp->sx)
sb_w = wp->sx;
if (sb_w != 0 &&
w->sb_pos ==
PANE_SCROLLBARS_LEFT) {
if (s->cx < sb_w)
if (window_pane_scrollbar_overlay_visible(wp)) {
sb_w = wp->scrollbar_style.width;
if (sb_w > wp->sx)
sb_w = wp->sx;
if (sb_w != 0 &&
w->sb_pos == PANE_SCROLLBARS_LEFT) {
if (s->cx < sb_w)
cursor = 0;
} else if (sb_w != 0 &&
s->cx >= wp->sx - sb_w)
cursor = 0;
} else if (sb_w != 0 &&
s->cx >= wp->sx - sb_w)
cursor = 0;
}
}
if (status_at_line(c) == 0)
cy += status_line_size(c);
@@ -2089,10 +2088,10 @@ server_client_reset_state(struct client *c)
mode |= MODE_MOUSE_ALL;
}
}
if (options_get_number(oo, "focus-follows-mouse") ||
w->sb == PANE_SCROLLBARS_MODAL ||
w->sb == PANE_SCROLLBARS_AUTOHIDE)
mode |= MODE_MOUSE_ALL;
if (options_get_number(oo, "focus-follows-mouse") ||
w->sb == PANE_SCROLLBARS_MODAL ||
w->sb == PANE_SCROLLBARS_AUTOHIDE)
mode |= MODE_MOUSE_ALL;
else if (~mode & MODE_MOUSE_ALL)
mode |= MODE_MOUSE_BUTTON;
}

View File

@@ -104,7 +104,7 @@ spawn_window(struct spawn_context *sc, char **cause)
sc->wp0 = TAILQ_FIRST(&w->panes);
TAILQ_REMOVE(&w->panes, sc->wp0, entry);
layout_free(w);
layout_free(w, 0);
window_destroy_panes(w);
TAILQ_INSERT_HEAD(&w->panes, sc->wp0, entry);

6
tmux.1
View File

@@ -6777,6 +6777,12 @@ results in
replaces a
.Nm
colour by its six-digit hexadecimal RGB value.
If an argument of
.Ql f
or
.Ql b
is given, it will instead produce the SGR escape sequence to set the foreground
or background colour respectively.
.Pp
A limit may be placed on the length of the resultant string by prefixing it
by an

14
tmux.h
View File

@@ -1295,11 +1295,8 @@ struct window_pane {
#define PANE_UNSEENCHANGES 0x4000
#define PANE_REDRAWSCROLLBAR 0x8000
/* Last floating position/size, saved when the pane is tiled. */
int saved_float_xoff;
int saved_float_yoff;
u_int saved_float_sx;
u_int saved_float_sy;
bitstr_t *sync_dirty;
u_int sync_dirty_size;
u_int sb_slider_y;
u_int sb_slider_h;
@@ -3307,6 +3304,7 @@ void colour_split_rgb(int, u_char *, u_char *, u_char *);
int colour_force_rgb(int);
int colour_dim(int, u_int);
const char *colour_tostring(int);
const char *colour_toescape(struct client *, int, int);
enum client_theme colour_totheme(int);
int colour_fromstring(const char *);
const char *colour_theme_option(u_int, enum client_theme);
@@ -3331,6 +3329,7 @@ bitstr_t *fuzzy_match(const char *, const char *, u_int, u_int *);
/* grid.c */
extern const struct grid_cell grid_default_cell;
void grid_check_is_clear(struct grid *);
void grid_empty_line(struct grid *, u_int, u_int);
void grid_set_tab(struct grid_cell *, u_int);
int grid_cells_equal(const struct grid_cell *, const struct grid_cell *);
@@ -3448,6 +3447,7 @@ void screen_write_mode_set(struct screen_write_ctx *, int);
void screen_write_mode_clear(struct screen_write_ctx *, int);
void screen_write_start_sync(struct window_pane *);
void screen_write_stop_sync(struct window_pane *);
void screen_write_clear_dirty(struct window_pane *);
void screen_write_cursorup(struct screen_write_ctx *, u_int);
void screen_write_cursordown(struct screen_write_ctx *, u_int);
void screen_write_cursorright(struct screen_write_ctx *, u_int);
@@ -3683,7 +3683,7 @@ struct visible_ranges *window_visible_ranges(struct window_pane *, int, int,
/* layout.c */
u_int layout_count_cells(struct layout_cell *);
struct layout_cell *layout_create_cell(struct layout_cell *);
void layout_free_cell(struct layout_cell *);
void layout_free_cell(struct layout_cell *, int);
void layout_print_cell(struct layout_cell *, const char *, u_int);
void layout_destroy_cell(struct window *, struct layout_cell *,
struct layout_cell **);
@@ -3706,7 +3706,7 @@ void layout_resize_set_size(struct window *, struct layout_cell *,
enum layout_type, u_int);
struct layout_cell *layout_cell_get_neighbour(struct layout_cell *);
void layout_init(struct window *, struct window_pane *);
void layout_free(struct window *);
void layout_free(struct window *, int);
void layout_resize(struct window *, u_int, u_int);
void layout_resize_pane(struct window_pane *, enum layout_type,
int, int);

View File

@@ -583,6 +583,19 @@ tty_default_features(int *feat, const char *name, u_int version)
"hyperlinks,"
"usstyle"
},
{ .name = "ghostty",
.features = TTY_FEATURES_BASE_MODERN_XTERM ","
"ccolour,"
"cstyle,"
"extkeys,"
"focus,"
"overline,"
"hyperlinks,"
"osc7,"
"sync,"
"usstyle,"
"progressbar"
},
{ .name = "XTerm",
/*
* xterm also supports DECSLRM and DECFRA, but they can be

View File

@@ -1663,6 +1663,8 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
tty_default_features(features, "foot", 0);
else if (strncmp(tmp, "WezTerm ", 7) == 0)
tty_default_features(features, "WezTerm", 0);
else if (strncmp(tmp, "ghostty ", 8) == 0)
tty_default_features(features, "ghostty", 0);
log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf);
free(c->term_type);

View File

@@ -357,8 +357,8 @@ window_destroy(struct window *w)
window_unzoom(w, 0);
RB_REMOVE(windows, &windows, w);
layout_free_cell(w->layout_root);
layout_free_cell(w->saved_layout_root);
layout_free_cell(w->layout_root, 0);
layout_free_cell(w->saved_layout_root, 0);
free(w->old_layout);
window_destroy_panes(w);
@@ -795,7 +795,7 @@ window_unzoom(struct window *w, int notify)
return (-1);
w->flags &= ~WINDOW_ZOOMED;
layout_free(w);
layout_free(w, 0);
w->layout_root = w->saved_layout_root;
w->saved_layout_root = NULL;
@@ -1238,6 +1238,7 @@ window_pane_destroy(struct window_pane *wp)
window_pane_clear_prompt(wp);
window_pane_free_modes(wp);
screen_write_clear_dirty(wp);
free(wp->searchstr);
if (wp->fd != -1) {