diff --git a/format.c b/format.c index 1eb88b1f8..1f784d9bc 100644 --- a/format.c +++ b/format.c @@ -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 @@ -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; - width = format_width(text); - if (width == 0) - 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")); diff --git a/fuzzy.c b/fuzzy.c index f28e47bbf..4655f9d94 100644 --- a/fuzzy.c +++ b/fuzzy.c @@ -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 @@ -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); } diff --git a/screen-redraw.c b/screen-redraw.c index 0cbda0a3c..3627d6e71 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -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 @@ -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. */ diff --git a/spawn.c b/spawn.c index 4717b55c1..627e5813c 100644 --- a/spawn.c +++ b/spawn.c @@ -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 @@ -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); diff --git a/window.c b/window.c index e290bb1f7..65ead732a 100644 --- a/window.c +++ b/window.c @@ -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 @@ -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);