Merge remote-tracking branch 'refs/remotes/tmux-openbsd/master'

* refs/remotes/tmux-openbsd/master:
  Clear stale pane references correctly when respawning windows, GitHub issue 5757 from Alejandro Pacheco.
  Add a timeout for fuzzy matching, also some general tidying up.
  Don't bother working out width for fuzzy match without positions since it is not needed.
This commit is contained in:
tmux update bot
2026-10-16 16:28:17 +00:00
5 changed files with 223 additions and 158 deletions

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: format.c,v 1.430 2026/10/16 07:59:06 nicm Exp $ */
/* $OpenBSD: format.c,v 1.431 2026/10/16 12:21:12 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -5102,11 +5102,13 @@ format_match_fuzzy(const char *pattern, const char *text, int positions)
bitstr_t *bs;
char *value;
size_t size;
u_int i, width;
u_int i, width = 1;
if (positions) {
width = format_width(text);
if (width == 0)
width = 1;
}
bs = fuzzy_match(pattern, text, width, NULL);
if (bs == NULL)
return (xstrdup(positions ? "" : "0"));

321
fuzzy.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: fuzzy.c,v 1.2 2026/09/28 16:55:08 nicm Exp $ */
/* $OpenBSD: fuzzy.c,v 1.3 2026/10/16 12:28:43 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -59,6 +59,23 @@
#define FUZZY_PENALTY_LEADING 1
#define FUZZY_PENALTY_LEADING_MAX 10
#define FUZZY_PENALTY_GAP 1
#define FUZZY_TIME_LIMIT 100
#define FUZZY_TIME_LOOP_CHECK 10000
/* State for matching. */
struct fuzzy_state {
uint64_t end_time;
u_int check;
int stopped;
struct fuzzy_char *cs;
u_int ncs;
u_int alloc;
u_int widths[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
struct utf8_data *tok;
u_int toklen;
int fold;
char *matched;
};
/* A single visible character of the text. */
struct fuzzy_char {
@@ -78,9 +95,21 @@ struct fuzzy_term {
size_t len;
};
/* Check whether a match has taken too long. */
static int
fuzzy_check_time(struct fuzzy_state *fs)
{
if (++fs->check % FUZZY_TIME_LOOP_CHECK != 0)
return (1);
if (get_timer() < fs->end_time)
return (1);
fs->stopped = 1;
return (0);
}
/* Is this character a word boundary, so a match after it scores higher? */
static int
fuzzy_is_boundary(const struct utf8_data *ud)
fuzzy_is_boundary(struct utf8_data *ud)
{
static const char *boundary = " -_/.:";
@@ -94,7 +123,7 @@ fuzzy_is_boundary(const struct utf8_data *ud)
* directly without case folding.
*/
static int
fuzzy_char_equal(const struct utf8_data *a, const struct utf8_data *b, int fold)
fuzzy_char_equal(struct utf8_data *a, struct utf8_data *b, int fold)
{
if (fold &&
a->size == 1 &&
@@ -116,21 +145,20 @@ fuzzy_align(enum style_align align)
/* Add a visible character to the array, updating the alignment width. */
static void
fuzzy_add(struct fuzzy_char **cs, u_int *ncs, u_int *alloc, enum style_align a,
const struct utf8_data *ud, u_int *widths)
fuzzy_add(struct fuzzy_state *fs, enum style_align a, struct utf8_data *ud)
{
struct fuzzy_char *fc;
if (*ncs == *alloc) {
*alloc = (*alloc == 0) ? 64 : *alloc * 2;
*cs = xreallocarray(*cs, *alloc, sizeof **cs);
if (fs->ncs == fs->alloc) {
fs->alloc = (fs->alloc == 0) ? 64 : fs->alloc * 2;
fs->cs = xreallocarray(fs->cs, fs->alloc, sizeof *fs->cs);
}
fc = &(*cs)[(*ncs)++];
fc = &fs->cs[fs->ncs++];
fc->align = a;
memcpy(&fc->ud, ud, sizeof fc->ud);
fc->width = ud->width;
fc->offset = widths[a];
widths[a] += ud->width;
fc->offset = fs->widths[a];
fs->widths[a] += ud->width;
}
/* Decode a character as UTF-8. */
@@ -153,14 +181,13 @@ fuzzy_decode_one(const char *cp, const char *end, struct utf8_data *ud)
/*
* Scan the text into an array of visible characters, skipping styles and
* recording the alignment and intra-alignment offset of each. Returns the
* array and its length and fills in the per-alignment widths.
* recording the alignment and intra-alignment offset of each. Fills in the
* array, its length and the per-alignment widths in the match state.
*/
static struct fuzzy_char *
fuzzy_scan(const char *text, u_int *ncs, u_int *widths)
static void
fuzzy_scan(struct fuzzy_state *fs, const char *text)
{
struct fuzzy_char *cs = NULL;
u_int alloc = 0, n, leading, i;
u_int n, leading, i;
enum style_align current = STYLE_ALIGN_LEFT;
struct style sy;
const char *cp = text, *textend = text + strlen(text);
@@ -168,35 +195,39 @@ fuzzy_scan(const char *text, u_int *ncs, u_int *widths)
struct utf8_data ud, hash, bracket;
char *tmp;
*ncs = 0;
memset(widths, 0, sizeof *widths * (STYLE_ALIGN_ABSOLUTE_CENTRE + 1));
style_set(&sy, &grid_default_cell);
utf8_set(&hash, '#');
utf8_set(&bracket, '[');
while (*cp != '\0') {
if (!fuzzy_check_time(fs))
break;
/* Handle a run of #s, which may introduce a style. */
if (*cp == '#') {
for (n = 0; cp[n] == '#'; n++)
/* nothing */;
for (n = 0; cp[n] == '#'; n++) {
if (!fuzzy_check_time(fs))
return;
}
if (cp[n] != '[') {
/* Escaped #s: ##->#, so half (rounded up). */
leading = (n % 2 == 0) ? n / 2 : n / 2 + 1;
for (i = 0; i < leading; i++) {
fuzzy_add(&cs, ncs, &alloc, current,
&hash, widths);
if (!fuzzy_check_time(fs))
return;
fuzzy_add(fs, current, &hash);
}
cp += n;
continue;
}
/* Even count: all #s escaped, the [ is literal. */
for (i = 0; i < n / 2; i++)
fuzzy_add(&cs, ncs, &alloc, current, &hash,
widths);
for (i = 0; i < n / 2; i++) {
if (!fuzzy_check_time(fs))
return;
fuzzy_add(fs, current, &hash);
}
if (n % 2 == 0) {
fuzzy_add(&cs, ncs, &alloc, current, &bracket,
widths);
fuzzy_add(fs, current, &bracket);
cp += n + 1;
continue;
}
@@ -223,9 +254,8 @@ fuzzy_scan(const char *text, u_int *ncs, u_int *widths)
*/
if (ud.size == 1 && (ud.data[0] <= 0x1f || ud.data[0] >= 0x7f))
continue;
fuzzy_add(&cs, ncs, &alloc, current, &ud, widths);
fuzzy_add(fs, current, &ud);
}
return (cs);
}
/*
@@ -234,8 +264,8 @@ fuzzy_scan(const char *text, u_int *ncs, u_int *widths)
* character is visible, otherwise returns -1.
*/
static int
fuzzy_column(const struct fuzzy_char *fc, const u_int *start, const u_int *src,
const u_int *vis, u_int *column)
fuzzy_column(struct fuzzy_char *fc, u_int *start, u_int *src, u_int *vis,
u_int *column)
{
enum style_align a = fc->align;
@@ -245,35 +275,34 @@ fuzzy_column(const struct fuzzy_char *fc, const u_int *start, const u_int *src,
return (0);
}
/* Decode a UTF-8 term, stopping if it cannot fit in the given limit. */
/* Decode a UTF-8 term, stopping if it has more characters than the text. */
static int
fuzzy_decode(const char *tok, size_t len, struct utf8_data *out, u_int limit,
u_int *n)
fuzzy_decode(struct fuzzy_state *fs, const char *tok, size_t len)
{
const char *cp = tok, *end = tok + len;
*n = 0;
fs->toklen = 0;
while (cp != end) {
if (*n == limit)
if (fs->toklen == fs->ncs)
return (-1);
cp = fuzzy_decode_one(cp, end, &out[(*n)++]);
cp = fuzzy_decode_one(cp, end, &fs->tok[fs->toklen++]);
}
return (0);
}
/* Add the score for a fuzzy token matched at the given positions. */
static int
fuzzy_score_positions(const u_int *pos, u_int npos, const struct fuzzy_char *cs)
fuzzy_score_positions(struct fuzzy_state *fs, u_int *pos)
{
u_int i, gap, span;
int score = 0;
if (npos == 0)
if (fs->toklen == 0)
return (0);
if (pos[0] == 0)
score += FUZZY_BONUS_START;
else {
if (fuzzy_is_boundary(&cs[pos[0] - 1].ud))
if (fuzzy_is_boundary(&fs->cs[pos[0] - 1].ud))
score += FUZZY_BONUS_BOUNDARY;
if (pos[0] < FUZZY_PENALTY_LEADING_MAX)
score -= pos[0] * FUZZY_PENALTY_LEADING;
@@ -282,14 +311,14 @@ fuzzy_score_positions(const u_int *pos, u_int npos, const struct fuzzy_char *cs)
FUZZY_PENALTY_LEADING;
}
}
for (i = 1; i < npos; i++) {
for (i = 1; i < fs->toklen; i++) {
if (pos[i] == pos[i - 1] + 1)
score += FUZZY_BONUS_CONSECUTIVE;
else if (fuzzy_is_boundary(&cs[pos[i] - 1].ud))
else if (fuzzy_is_boundary(&fs->cs[pos[i] - 1].ud))
score += FUZZY_BONUS_BOUNDARY;
}
span = pos[npos - 1] - pos[0] + 1;
gap = span - npos;
span = pos[fs->toklen - 1] - pos[0] + 1;
gap = span - fs->toklen;
score -= gap * FUZZY_PENALTY_GAP;
return (score);
}
@@ -299,23 +328,27 @@ fuzzy_score_positions(const u_int *pos, u_int npos, const struct fuzzy_char *cs)
* token matches.
*/
static int
fuzzy_match_fuzzy(const struct utf8_data *tok, u_int toklen,
struct fuzzy_char *cs, u_int ncs, int fold, int *score, char *matched)
fuzzy_match_fuzzy(struct fuzzy_state *fs, int *score)
{
u_int pi, ci, *pos;
int found, value;
if (toklen == 0 || ncs == 0)
if (fs->toklen == 0 || fs->ncs == 0)
return (0);
pos = xcalloc(toklen, sizeof *pos);
pos = xcalloc(fs->toklen, sizeof *pos);
/* First find a subsequence from the start. */
ci = 0;
for (pi = 0; pi < toklen; pi++) {
while (ci != ncs &&
!fuzzy_char_equal(&tok[pi], &cs[ci].ud, fold))
for (pi = 0; pi < fs->toklen; pi++) {
while (ci != fs->ncs &&
!fuzzy_char_equal(&fs->tok[pi], &fs->cs[ci].ud, fs->fold)) {
if (!fuzzy_check_time(fs)) {
free(pos);
return (0);
}
ci++;
if (ci == ncs) {
}
if (ci == fs->ncs) {
free(pos);
return (0);
}
@@ -323,11 +356,16 @@ fuzzy_match_fuzzy(const struct utf8_data *tok, u_int toklen,
}
/* Then compact it backwards to prefer a shorter span. */
ci = pos[toklen - 1];
for (pi = toklen; pi > 0; pi--) {
ci = pos[fs->toklen - 1];
for (pi = fs->toklen; pi > 0; pi--) {
found = 0;
for (;;) {
if (fuzzy_char_equal(&tok[pi - 1], &cs[ci].ud, fold)) {
if (!fuzzy_check_time(fs)) {
free(pos);
return (0);
}
if (fuzzy_char_equal(&fs->tok[pi - 1], &fs->cs[ci].ud,
fs->fold)) {
pos[pi - 1] = ci;
found = 1;
break;
@@ -344,78 +382,78 @@ fuzzy_match_fuzzy(const struct utf8_data *tok, u_int toklen,
ci--;
}
value = fuzzy_score_positions(pos, toklen, cs);
value = fuzzy_score_positions(fs, pos);
*score += value;
for (pi = 0; pi < toklen; pi++)
matched[pos[pi]] = 1;
for (pi = 0; pi < fs->toklen; pi++)
fs->matched[pos[pi]] = 1;
free(pos);
return (1);
}
/* Score an exact, prefix or suffix match. */
static int
fuzzy_score_exact(u_int start, u_int toklen, u_int ncs,
const struct fuzzy_char *cs, int prefix, int suffix)
fuzzy_score_exact(struct fuzzy_state *fs, struct fuzzy_term *term, u_int start)
{
int score;
score = FUZZY_BONUS_EXACT + toklen * FUZZY_BONUS_CONSECUTIVE;
if (prefix)
score = FUZZY_BONUS_EXACT + fs->toklen * FUZZY_BONUS_CONSECUTIVE;
if (term->prefix)
score += FUZZY_BONUS_PREFIX;
if (suffix)
if (term->suffix)
score += FUZZY_BONUS_SUFFIX;
if (start == 0)
score += FUZZY_BONUS_START;
else if (fuzzy_is_boundary(&cs[start - 1].ud))
else if (fuzzy_is_boundary(&fs->cs[start - 1].ud))
score += FUZZY_BONUS_BOUNDARY;
if (start < FUZZY_PENALTY_LEADING_MAX)
score -= start * FUZZY_PENALTY_LEADING;
else
score -= FUZZY_PENALTY_LEADING_MAX * FUZZY_PENALTY_LEADING;
if (!prefix && !suffix)
score -= ncs - (start + toklen);
if (!term->prefix && !term->suffix)
score -= fs->ncs - (start + fs->toklen);
return (score);
}
/* Match an exact, prefix or suffix term against the visible characters. */
static int
fuzzy_match_exact(const struct utf8_data *tok, u_int toklen,
struct fuzzy_char *cs, u_int ncs, int fold, int prefix, int suffix,
int *score, char *matched)
fuzzy_match_exact(struct fuzzy_state *fs, struct fuzzy_term *term, int *score)
{
u_int start, end, i, j, best = 0;
int ok, found = 0, value, bestscore = 0;
if (toklen == 0 || toklen > ncs)
if (fs->toklen == 0 || fs->toklen > fs->ncs)
return (0);
if (prefix && suffix) {
if (toklen != ncs)
if (term->prefix && term->suffix) {
if (fs->toklen != fs->ncs)
return (0);
start = 0;
end = 1;
} else if (prefix) {
} else if (term->prefix) {
start = 0;
end = 1;
} else if (suffix) {
start = ncs - toklen;
} else if (term->suffix) {
start = fs->ncs - fs->toklen;
end = start + 1;
} else {
start = 0;
end = ncs - toklen + 1;
end = fs->ncs - fs->toklen + 1;
}
for (i = start; i < end; i++) {
ok = 1;
for (j = 0; j < toklen; j++) {
if (!fuzzy_char_equal(&tok[j], &cs[i + j].ud, fold)) {
for (j = 0; j < fs->toklen; j++) {
if (!fuzzy_check_time(fs))
return (0);
if (!fuzzy_char_equal(&fs->tok[j], &fs->cs[i + j].ud,
fs->fold)) {
ok = 0;
break;
}
}
if (!ok)
continue;
value = fuzzy_score_exact(i, toklen, ncs, cs, prefix, suffix);
value = fuzzy_score_exact(fs, term, i);
if (!found || value > bestscore) {
found = 1;
best = i;
@@ -425,9 +463,9 @@ fuzzy_match_exact(const struct utf8_data *tok, u_int toklen,
if (!found)
return (0);
*score += bestscore;
if (matched != NULL) {
for (i = 0; i < toklen; i++)
matched[best + i] = 1;
if (!term->inverse) {
for (i = 0; i < fs->toklen; i++)
fs->matched[best + i] = 1;
}
return (1);
}
@@ -472,22 +510,18 @@ fuzzy_parse_term(const char *start, const char *end, struct fuzzy_term *term)
/* Match one parsed term. */
static int
fuzzy_match_term(const struct fuzzy_term *term, struct utf8_data *tok,
struct fuzzy_char *cs, u_int ncs, int fold, int *score, char *matched)
fuzzy_match_term(struct fuzzy_state *fs, struct fuzzy_term *term, int *score)
{
u_int toklen;
int value = 0, matched_term;
if (fuzzy_decode(term->text, term->len, tok, ncs, &toklen) != 0)
if (fuzzy_decode(fs, term->text, term->len) != 0)
return (term->inverse);
if (term->exact) {
matched_term = fuzzy_match_exact(tok, toklen, cs, ncs, fold,
term->prefix, term->suffix, &value,
term->inverse ? NULL : matched);
} else {
matched_term = fuzzy_match_fuzzy(tok, toklen, cs, ncs, fold,
&value, term->inverse ? NULL : matched);
}
if (term->exact)
matched_term = fuzzy_match_exact(fs, term, &value);
else
matched_term = fuzzy_match_fuzzy(fs, &value);
if (fs->stopped)
return (0);
if (term->inverse)
return (!matched_term);
@@ -499,8 +533,8 @@ fuzzy_match_term(const struct fuzzy_term *term, struct utf8_data *tok,
/* Match one AND group of terms. */
static int
fuzzy_match_group(const char *start, const char *end, struct utf8_data *tok,
struct fuzzy_char *cs, u_int ncs, int fold, int *score, char *matched)
fuzzy_match_group(struct fuzzy_state *fs, const char *start, const char *end,
int *score)
{
const char *cp = start, *sp;
struct fuzzy_term term;
@@ -518,8 +552,7 @@ fuzzy_match_group(const char *start, const char *end, struct utf8_data *tok,
if (!fuzzy_parse_term(sp, cp, &term))
return (0);
any = 1;
if (!fuzzy_match_term(&term, tok, cs, ncs, fold, score,
matched))
if (!fuzzy_match_term(fs, &term, score))
return (0);
}
return (any);
@@ -534,19 +567,20 @@ fuzzy_match_group(const char *start, const char *end, struct utf8_data *tok,
bitstr_t *
fuzzy_match(const char *pattern, const char *text, u_int width, u_int *score)
{
struct fuzzy_char *cs;
char *matched = NULL, *best = NULL, *groupmatched;
struct utf8_data *tok;
bitstr_t *mask;
u_int ncs, i, j, column;
u_int widths[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
struct fuzzy_state fs;
char *best = NULL;
bitstr_t *mask = NULL;
u_int i, j, column;
u_int start[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
u_int src[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
u_int vis[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
u_int wl, wc, wr, wa;
size_t toksize;
size_t toksize, size;
const char *cp, *sp;
int bestscore = 0, groupscore, found = 0, fold;
int bestscore = 0, groupscore, found = 0;
memset(&fs, 0, sizeof fs);
fs.end_time = get_timer() + FUZZY_TIME_LIMIT;
if (width == 0)
return (NULL);
@@ -561,24 +595,27 @@ fuzzy_match(const char *pattern, const char *text, u_int width, u_int *score)
}
/* Smart-case: fold unless the pattern has an uppercase character. */
fold = 1;
fs.fold = 1;
for (cp = pattern; *cp != '\0'; cp++) {
if (*cp >= 'A' && *cp <= 'Z') {
fold = 0;
fs.fold = 0;
break;
}
}
/* Scan the text into visible characters. */
cs = fuzzy_scan(text, &ncs, widths);
matched = xcalloc(ncs == 0 ? 1 : ncs, sizeof *matched);
best = xcalloc(ncs == 0 ? 1 : ncs, sizeof *best);
fuzzy_scan(&fs, text);
if (fs.stopped)
goto fail;
size = fs.ncs == 0 ? 1 : fs.ncs;
fs.matched = xcalloc(size, sizeof *fs.matched);
best = xcalloc(size, sizeof *best);
/* A term with more characters than the text cannot match. */
toksize = strlen(pattern);
if (toksize > ncs)
toksize = ncs;
tok = xreallocarray(NULL, toksize == 0 ? 1 : toksize, sizeof *tok);
if (toksize > fs.ncs)
toksize = fs.ncs;
fs.tok = xreallocarray(NULL, toksize == 0 ? 1 : toksize, sizeof *fs.tok);
/* Match each |-separated group and keep the best-scoring one. */
cp = pattern;
@@ -590,34 +627,31 @@ fuzzy_match(const char *pattern, const char *text, u_int width, u_int *score)
sp = cp;
while (*cp != '\0' && *cp != '|')
cp++;
memset(matched, 0, ncs == 0 ? 1 : ncs);
groupmatched = matched;
if (fuzzy_match_group(sp, cp, tok, cs, ncs, fold,
&groupscore, groupmatched)) {
memset(fs.matched, 0, size);
if (fuzzy_match_group(&fs, sp, cp, &groupscore)) {
if (!found || groupscore > bestscore) {
found = 1;
bestscore = groupscore;
memcpy(best, matched, ncs == 0 ? 1 : ncs);
memcpy(best, fs.matched, size);
}
}
if (fs.stopped)
break;
}
free(tok);
if (!found) {
free(best);
free(matched);
free(cs);
return (NULL);
}
if (fs.stopped || !found)
goto fail;
/*
* Work out the trimmed widths and start columns of each alignment,
* mirroring format_draw_none.
*/
wl = widths[STYLE_ALIGN_LEFT];
wc = widths[STYLE_ALIGN_CENTRE];
wr = widths[STYLE_ALIGN_RIGHT];
wa = widths[STYLE_ALIGN_ABSOLUTE_CENTRE];
wl = fs.widths[STYLE_ALIGN_LEFT];
wc = fs.widths[STYLE_ALIGN_CENTRE];
wr = fs.widths[STYLE_ALIGN_RIGHT];
wa = fs.widths[STYLE_ALIGN_ABSOLUTE_CENTRE];
while (wl + wc + wr > width) {
if (!fuzzy_check_time(&fs))
goto fail;
if (wc > 0)
wc--;
else if (wr > 0)
@@ -633,12 +667,12 @@ fuzzy_match(const char *pattern, const char *text, u_int width, u_int *score)
vis[STYLE_ALIGN_LEFT] = wl;
start[STYLE_ALIGN_RIGHT] = width - wr;
src[STYLE_ALIGN_RIGHT] = widths[STYLE_ALIGN_RIGHT] - wr;
src[STYLE_ALIGN_RIGHT] = fs.widths[STYLE_ALIGN_RIGHT] - wr;
vis[STYLE_ALIGN_RIGHT] = wr;
start[STYLE_ALIGN_CENTRE] =
wl + ((width - wr) - wl) / 2 - wc / 2;
src[STYLE_ALIGN_CENTRE] = widths[STYLE_ALIGN_CENTRE] / 2 - wc / 2;
src[STYLE_ALIGN_CENTRE] = fs.widths[STYLE_ALIGN_CENTRE] / 2 - wc / 2;
vis[STYLE_ALIGN_CENTRE] = wc;
start[STYLE_ALIGN_ABSOLUTE_CENTRE] = (width - wa) / 2;
@@ -647,20 +681,31 @@ fuzzy_match(const char *pattern, const char *text, u_int width, u_int *score)
/* Set a bit for each column of each matched character. */
mask = bit_alloc(width);
for (i = 0; i < ncs; i++) {
for (i = 0; i < fs.ncs; i++) {
if (!fuzzy_check_time(&fs))
goto fail;
if (!best[i])
continue;
if (fuzzy_column(&cs[i], start, src, vis, &column) != 0)
if (fuzzy_column(&fs.cs[i], start, src, vis, &column) != 0)
continue;
for (j = 0; j < cs[i].width && column + j < width; j++)
for (j = 0; j < fs.cs[i].width && column + j < width; j++)
bit_set(mask, column + j);
}
free(best);
free(matched);
free(cs);
free(fs.tok);
free(fs.matched);
free(fs.cs);
if (score != NULL)
*score = (bestscore < 0) ? 0 : (u_int)bestscore;
return (mask);
fail:
free(mask);
free(best);
free(fs.tok);
free(fs.matched);
free(fs.cs);
return (NULL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: screen-redraw.c,v 1.169 2026/10/16 07:59:06 nicm Exp $ */
/* $OpenBSD: screen-redraw.c,v 1.170 2026/10/16 14:26:08 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1096,11 +1096,17 @@ 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. */
/*
* Mark a window's cached redraw scenes as out of date. Generations are unique
* across all windows so a scene can never match a new window that reuses the
* address of a freed one.
*/
void
redraw_invalidate_scene(struct window *w)
{
w->redraw_scene_generation++;
static uint64_t next_generation;
w->redraw_scene_generation = ++next_generation;
}
/* Free all pending damage for a window. */

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: spawn.c,v 1.57 2026/10/08 07:50:05 nicm Exp $ */
/* $OpenBSD: spawn.c,v 1.58 2026/10/16 14:26:08 nicm Exp $ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -140,6 +140,7 @@ spawn_window(struct spawn_context *sc, char **cause)
}
}
window_unzoom(w, 1);
sc->wp0 = TAILQ_FIRST(&w->panes);
TAILQ_REMOVE(&w->panes, sc->wp0, entry);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: window.c,v 1.387 2026/10/16 11:34:49 bket Exp $ */
/* $OpenBSD: window.c,v 1.388 2026/10/16 14:26:08 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -445,6 +445,7 @@ window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel)
w->id = next_window_id++;
RB_INSERT(windows, &windows, w);
redraw_invalidate_scene(w);
if (gettimeofday(&w->creation_time, NULL) != 0)
fatal("gettimeofday failed");
@@ -1196,6 +1197,19 @@ window_add_pane(struct window *w, struct window_pane *other, u_int hlimit,
return (wp);
}
static void
window_forget_pane(struct window *w, struct window_pane *wp)
{
if (wp == marked_pane.wp)
server_clear_marked();
if (wp == w->modal_last)
w->modal_last = NULL;
if (wp == w->was_zoomed)
w->was_zoomed = NULL;
window_pane_stack_remove(&w->last_panes, wp);
redraw_invalidate_scene(w);
}
void
window_lost_pane(struct window *w, struct window_pane *wp)
{
@@ -1203,14 +1217,8 @@ window_lost_pane(struct window *w, struct window_pane *wp)
log_debug("%s: @%u pane %%%u", __func__, w->id, wp->id);
if (wp == marked_pane.wp)
server_clear_marked();
if (wp == w->modal_last)
w->modal_last = NULL;
if (wp == w->was_zoomed)
w->was_zoomed = NULL;
window_forget_pane(w, wp);
window_pane_stack_remove(&w->last_panes, wp);
if (wp == w->active) {
lastwp = NULL;
if (wp == w->modal) {
@@ -1233,10 +1241,8 @@ window_lost_pane(struct window *w, struct window_pane *wp)
window_fire_pane_changed(w, w->active, wp);
window_update_focus(w);
}
} else if (wp == w->modal) {
} else if (wp == w->modal)
w->modal = w->modal_last = NULL;
}
redraw_invalidate_scene(w);
}
void
@@ -1364,6 +1370,11 @@ window_destroy_panes(struct window *w)
while (!TAILQ_EMPTY(&w->panes)) {
wp = TAILQ_FIRST(&w->panes);
window_forget_pane(w, wp);
if (wp == w->active)
w->active = NULL;
if (wp == w->modal)
w->modal = NULL;
TAILQ_REMOVE(&w->panes, wp, entry);
TAILQ_REMOVE(&w->z_index, wp, zentry);
window_pane_destroy(wp);