From 6b26f0abc074964e876b13cabf5181ca0aecca34 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 14 Oct 2026 11:54:55 +0000 Subject: [PATCH 01/10] Only redraw when a feature changed. --- tty.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tty.c b/tty.c index b8531c7e6..982e65669 100644 --- a/tty.c +++ b/tty.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tty.c,v 1.487 2026/10/14 08:33:19 nicm Exp $ */ +/* $OpenBSD: tty.c,v 1.488 2026/10/14 11:54:55 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -551,9 +551,12 @@ void tty_update_features(struct tty *tty) { struct client *c = tty->client; + int changed = 0; - if (tty_apply_features(tty->term)) + if (tty_apply_features(tty->term)) { tty_term_apply_overrides(tty->term); + changed = 1; + } if (tty_use_margin(tty)) tty_putcode(tty, TTYC_ENMG); @@ -563,13 +566,10 @@ tty_update_features(struct tty *tty) tty_puts(tty, tty_term_string(tty->term, TTYC_ENFCS)); tty_puts(tty, tty_term_string(tty->term, TTYC_ENESC)); - /* - * Features might have changed since the first draw during attach. For - * example, this happens when DA responses are received. - */ - server_redraw_client(c); - - tty_invalidate(tty); + if (changed) { + server_redraw_client(c); + tty_invalidate(tty); + } } void From 3ba4fb32cf52ab5eb3441b947d3f15785b38db6a Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 14 Oct 2026 12:33:46 +0000 Subject: [PATCH 02/10] Add imagescroll feature and capability. --- tmux.1 | 8 ++++++-- tmux.h | 3 ++- tty-features.c | 15 ++++++++++++++- tty-term.c | 3 ++- window-client.c | 15 ++++++++------- 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/tmux.1 b/tmux.1 index ee852c221..6267b8227 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tmux.1,v 1.1185 2026/10/13 10:21:58 nicm Exp $ +.\" $OpenBSD: tmux.1,v 1.1186 2026/10/14 12:33:46 nicm Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -14,7 +14,7 @@ .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: October 13 2026 $ +.Dd $Mdocdate: October 14 2026 $ .Dt TMUX 1 .Os .Sh NAME @@ -5192,6 +5192,8 @@ Supports extended keys. Supports focus reporting. .It hyperlinks Supports OSC 8 hyperlinks. +.It imagescroll +Moves image content with scroll regions. .It ignorefkeys Ignore function keys from .Xr terminfo 5 @@ -8972,6 +8974,8 @@ These are set automatically if the capability is present. .It Em \&Hls Set or clear a hyperlink annotation. +.It Em \&Iscrl +Indicates that the terminal moves image content with scroll regions. .It Em \&Nobr Tell .Nm diff --git a/tmux.h b/tmux.h index c42115aef..4ea5e7671 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1461 2026/10/14 10:34:14 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1462 2026/10/14 12:33:46 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -491,6 +491,7 @@ enum tty_code_code { TTYC_IND, TTYC_INDN, TTYC_INVIS, + TTYC_ISCRL, TTYC_KCBT, TTYC_KCUB1, TTYC_KCUD1, diff --git a/tty-features.c b/tty-features.c index 80af558d0..ee0471b46 100644 --- a/tty-features.c +++ b/tty-features.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tty-features.c,v 1.48 2026/10/12 12:35:03 nicm Exp $ */ +/* $OpenBSD: tty-features.c,v 1.49 2026/10/14 12:33:46 nicm Exp $ */ /* * Copyright (c) 2020 Nicholas Marriott @@ -366,6 +366,17 @@ static const struct tty_feature tty_feature_kittygraphics = { TERM_KITTYGRAPHICS }; +/* Terminal moves images with scroll regions. */ +static const char *const tty_feature_imagescroll_capabilities[] = { + "Iscrl", + NULL +}; +static const struct tty_feature tty_feature_imagescroll = { + "imagescroll", + tty_feature_imagescroll_capabilities, + 0 +}; + /* Terminal supports the OSC 9;4 progress bar. */ static const char *const tty_feature_progressbar_capabilities[] = { "Spb=\\E]9;4;%p1%d;%p2%d\\E\\\\", @@ -396,6 +407,7 @@ static const struct tty_feature *const tty_features[] = { &tty_feature_extkeys, &tty_feature_focus, &tty_feature_ignorefkeys, + &tty_feature_imagescroll, &tty_feature_kittygraphics, &tty_feature_margins, &tty_feature_mouse, @@ -635,6 +647,7 @@ tty_default_features(struct client *c, const char *name, u_int version) "extkeys," "focus," "hyperlinks," + "imagescroll," "margins," "sixel," "usstyle" diff --git a/tty-term.c b/tty-term.c index e34a872b4..cb2a25f40 100644 --- a/tty-term.c +++ b/tty-term.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tty-term.c,v 1.112 2026/10/12 10:39:05 nicm Exp $ */ +/* $OpenBSD: tty-term.c,v 1.113 2026/10/14 12:33:46 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -113,6 +113,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_IND] = { TTYCODE_STRING, "ind" }, [TTYC_INDN] = { TTYCODE_STRING, "indn" }, [TTYC_INVIS] = { TTYCODE_STRING, "invis" }, + [TTYC_ISCRL] = { TTYCODE_FLAG, "Iscrl" }, [TTYC_KCBT] = { TTYCODE_STRING, "kcbt" }, [TTYC_KCUB1] = { TTYCODE_STRING, "kcub1" }, [TTYC_KCUD1] = { TTYCODE_STRING, "kcud1" }, diff --git a/window-client.c b/window-client.c index 05bb2f801..c9234847f 100644 --- a/window-client.c +++ b/window-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window-client.c,v 1.50 2026/10/12 12:35:03 nicm Exp $ */ +/* $OpenBSD: window-client.c,v 1.51 2026/10/14 12:33:46 nicm Exp $ */ /* * Copyright (c) 2017 Nicholas Marriott @@ -89,19 +89,20 @@ static const char *window_client_info_lines[] = { " #[#{E:tree-mode-border-style},acs]x#[default] " WINDOW_CLIENT_FEATURE(hyperlinks) " " WINDOW_CLIENT_FEATURE(ignorefkeys) " " - WINDOW_CLIENT_FEATURE(kittygraphics) " " - WINDOW_CLIENT_FEATURE(margins), + WINDOW_CLIENT_FEATURE(imagescroll) " " + WINDOW_CLIENT_FEATURE(kittygraphics), " #[#{E:tree-mode-border-style},acs]x#[default] " + WINDOW_CLIENT_FEATURE(margins) " " WINDOW_CLIENT_FEATURE(mouse) " " WINDOW_CLIENT_FEATURE(osc7) " " - WINDOW_CLIENT_FEATURE(overline) " " - WINDOW_CLIENT_FEATURE(progressbar), + WINDOW_CLIENT_FEATURE(overline), " #[#{E:tree-mode-border-style},acs]x#[default] " + WINDOW_CLIENT_FEATURE(progressbar) " " WINDOW_CLIENT_FEATURE(rectfill) " " WINDOW_CLIENT_FEATURE(sixel) " " - WINDOW_CLIENT_FEATURE(strikethrough) " " - WINDOW_CLIENT_FEATURE(sync), + WINDOW_CLIENT_FEATURE(strikethrough), " #[#{E:tree-mode-border-style},acs]x#[default] " + WINDOW_CLIENT_FEATURE(sync) " " WINDOW_CLIENT_FEATURE(title) " " WINDOW_CLIENT_FEATURE(usstyle) " " WINDOW_CLIENT_FEATURE(utf8), From abbe04fb74d62e6817a0dc8a1c0d5736e3d7425a Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 14 Oct 2026 12:38:18 +0000 Subject: [PATCH 03/10] Add flags to damage struct. --- screen-redraw.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 38a56b3ca..ad6f52d12 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1,4 +1,4 @@ -/* $OpenBSD: screen-redraw.c,v 1.164 2026/10/14 10:34:14 nicm Exp $ */ +/* $OpenBSD: screen-redraw.c,v 1.165 2026/10/14 12:38:18 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott @@ -218,7 +218,10 @@ struct redraw_damage { u_int sx; u_int sy; - TAILQ_ENTRY(redraw_damage) entry; + int flags; +#define REDRAW_DAMAGE_SCROLL 0x1 + + TAILQ_ENTRY(redraw_damage) entry; }; /* @@ -1134,6 +1137,7 @@ redraw_collapse_damage(struct window *w) x1 = rd->x + rd->sx; if (rd->y + rd->sy > y1) y1 = rd->y + rd->sy; + first->flags &= rd->flags; if (rd != first) { TAILQ_REMOVE(&w->damage, rd, entry); free(rd); @@ -1148,8 +1152,9 @@ redraw_collapse_damage(struct window *w) } /* Record window damage, merging nearby rectangles and limiting the count. */ -void -redraw_damage_window(struct window *w, u_int x, u_int y, u_int sx, u_int sy) +static void +redraw_apply_window_damage(struct window *w, u_int x, u_int y, u_int sx, + u_int sy, int flags) { struct redraw_damage *rd; u_int x0, y0, x1, y1, area, union_area; @@ -1182,6 +1187,7 @@ redraw_damage_window(struct window *w, u_int x, u_int y, u_int sx, u_int sy) rd->y = y0; rd->sx = x1 - x0; rd->sy = y1 - y0; + rd->flags &= flags; return; } @@ -1190,11 +1196,19 @@ redraw_damage_window(struct window *w, u_int x, u_int y, u_int sx, u_int sy) rd->y = y; rd->sx = sx; rd->sy = sy; + rd->flags = flags; TAILQ_INSERT_TAIL(&w->damage, rd, entry); if (++w->damage_count > REDRAW_DAMAGE_MAX) redraw_collapse_damage(w); } +/* Record ordinary window damage. */ +void +redraw_damage_window(struct window *w, u_int x, u_int y, u_int sx, u_int sy) +{ + redraw_apply_window_damage(w, x, y, sx, sy, 0); +} + /* Mark all cached redraw scenes as out of date. */ void redraw_invalidate_all_scenes(void) From 3e6c6576ab14a9aff8c7765c7fa5e0b979d601e6 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 14 Oct 2026 12:48:50 +0000 Subject: [PATCH 04/10] Track window scrolling for damage. --- screen-redraw.c | 36 +++++++++++++++++++++++++++++++++++- tmux.h | 10 +++++++++- tty.c | 10 +++++++++- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index ad6f52d12..b3c2f6f77 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1,4 +1,4 @@ -/* $OpenBSD: screen-redraw.c,v 1.165 2026/10/14 12:38:18 nicm Exp $ */ +/* $OpenBSD: screen-redraw.c,v 1.166 2026/10/14 12:48:50 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott @@ -1111,6 +1111,7 @@ redraw_free_damage(struct window *w) free(rd); } w->damage_count = 0; + w->scroll_pending = 0; } /* Collapse all pending damage for a window into one rectangle. */ @@ -1209,6 +1210,39 @@ redraw_damage_window(struct window *w, u_int x, u_int y, u_int sx, u_int sy) redraw_apply_window_damage(w, x, y, sx, sy, 0); } +/* Record damage caused by terminal scrolling. */ +void +redraw_damage_scroll(struct window *w, u_int x, u_int y, u_int sx, u_int sy) +{ + if (!w->scroll_pending) { + w->scroll_generation++; + w->scroll_pending = 1; + } + redraw_apply_window_damage(w, x, y, sx, sy, REDRAW_DAMAGE_SCROLL); +} + +/* Remember whether this client used terminal scrolling for the damage. */ +void +redraw_scroll_result(struct tty *tty, const struct tty_ctx *ctx, int failed) +{ + struct window_pane *wp = ctx->arg; + struct window *w; + + if (wp == NULL) + return; + w = wp->window; + if (!w->scroll_pending) + return; + if (tty->scroll_window != w || + tty->scroll_generation != w->scroll_generation) { + tty->scroll_window = w; + tty->scroll_generation = w->scroll_generation; + tty->scroll_failed = 0; + } + if (failed) + tty->scroll_failed = 1; +} + /* Mark all cached redraw scenes as out of date. */ void redraw_invalidate_all_scenes(void) diff --git a/tmux.h b/tmux.h index 4ea5e7671..f0c6ca61b 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1462 2026/10/14 12:33:46 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1463 2026/10/14 12:48:50 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1464,6 +1464,8 @@ struct window { struct redraw_damages damage; u_int damage_count; + uint64_t scroll_generation; + int scroll_pending; struct menu_data *menu; u_int menu_last_px; @@ -1835,6 +1837,10 @@ struct tty { struct event key_timer; struct tty_key *key_tree; + + struct window *scroll_window; + uint64_t scroll_generation; + int scroll_failed; }; /* Terminal command context. */ @@ -3620,6 +3626,8 @@ int redraw_client_has_window(struct client *, struct window *); void redraw_invalidate_scene(struct window *); void redraw_invalidate_all_scenes(void); void redraw_damage_window(struct window *, u_int, u_int, u_int, u_int); +void redraw_damage_scroll(struct window *, u_int, u_int, u_int, u_int); +void redraw_scroll_result(struct tty *, const struct tty_ctx *, int); void redraw_free_damage(struct window *); void redraw_client_damage(struct client *); int redraw_get_status_border_cell_type(struct redraw_span **, u_int); diff --git a/tty.c b/tty.c index 982e65669..feae568c4 100644 --- a/tty.c +++ b/tty.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tty.c,v 1.488 2026/10/14 11:54:55 nicm Exp $ */ +/* $OpenBSD: tty.c,v 1.489 2026/10/14 12:48:50 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1669,10 +1669,12 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) !tty_term_has(tty->term, TTYC_RIN)) || ctx->sx == 1 || ctx->sy == 1) { + redraw_scroll_result(tty, ctx, 1); tty_redraw_region(tty, ctx); return; } + redraw_scroll_result(tty, ctx, 0); tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); @@ -1697,10 +1699,12 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) !tty_term_has(tty->term, TTYC_CSR) || ctx->sx == 1 || ctx->sy == 1) { + redraw_scroll_result(tty, ctx, 1); tty_redraw_region(tty, ctx); return; } + redraw_scroll_result(tty, ctx, 0); tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); @@ -1735,10 +1739,12 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) !tty_term_has(tty->term, TTYC_CSR) || ctx->sx == 1 || ctx->sy == 1) { + redraw_scroll_result(tty, ctx, 1); tty_redraw_region(tty, ctx); return; } + redraw_scroll_result(tty, ctx, 0); tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); @@ -1773,10 +1779,12 @@ tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx) !tty_term_has(tty->term, TTYC_RIN)) || ctx->sx == 1 || ctx->sy == 1) { + redraw_scroll_result(tty, ctx, 1); tty_redraw_region(tty, ctx); return; } + redraw_scroll_result(tty, ctx, 0); tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); From 06262fbb48c7628ffdbd0b603df1b278e1a08a97 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 14 Oct 2026 12:51:00 +0000 Subject: [PATCH 05/10] Add a temporary var. --- screen-redraw.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index b3c2f6f77..26de8b599 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1,4 +1,4 @@ -/* $OpenBSD: screen-redraw.c,v 1.166 2026/10/14 12:48:50 nicm Exp $ */ +/* $OpenBSD: screen-redraw.c,v 1.167 2026/10/14 12:51:00 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott @@ -2191,6 +2191,7 @@ void redraw_client_damage(struct client *c) { struct window *w = c->session->curw->window; + struct tty *tty = &c->tty; struct window_pane *wp; struct redraw_scene *scene; struct redraw_draw_ctx dctx; @@ -2212,8 +2213,8 @@ redraw_client_damage(struct client *c) wp->active_border_gc_set = 0; } - tty_sync_start(&c->tty); - tty_update_mode(&c->tty, c->tty.mode & ~CURSOR_MODES, NULL); + tty_sync_start(tty); + tty_update_mode(tty, c->tty.mode & ~CURSOR_MODES, NULL); TAILQ_FOREACH(rd, &w->damage, entry) { x0 = (rd->x > ox) ? rd->x : ox; From fbde17083d632a3e88622ce517711574ce7f8fa0 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 14 Oct 2026 11:54:55 +0000 Subject: [PATCH 06/10] Only redraw when a feature changed. --- tty.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tty.c b/tty.c index 0f2e35a52..e9d839853 100644 --- a/tty.c +++ b/tty.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tty.c,v 1.487 2026/10/14 08:33:19 nicm Exp $ */ +/* $OpenBSD: tty.c,v 1.488 2026/10/14 11:54:55 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -560,12 +560,15 @@ void tty_update_features(struct tty *tty) { struct client *c = tty->client; + int changed = 0; - if (tty_apply_features(tty->term)) + if (tty_apply_features(tty->term)) { tty_term_apply_overrides(tty->term); #ifdef ENABLE_IMAGES - image_tty_update(tty); + image_tty_update(tty); #endif + changed = 1; + } if (tty_use_margin(tty)) tty_putcode(tty, TTYC_ENMG); @@ -575,13 +578,10 @@ tty_update_features(struct tty *tty) tty_puts(tty, tty_term_string(tty->term, TTYC_ENFCS)); tty_puts(tty, tty_term_string(tty->term, TTYC_ENESC)); - /* - * Features might have changed since the first draw during attach. For - * example, this happens when DA responses are received. - */ - server_redraw_client(c); - - tty_invalidate(tty); + if (changed) { + server_redraw_client(c); + tty_invalidate(tty); + } } void From ff12520ec08f1263a26f674eba99fc391dd9f1dd Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 14 Oct 2026 12:33:46 +0000 Subject: [PATCH 07/10] Add imagescroll feature and capability. --- tmux.1 | 8 ++++++-- tmux.h | 3 ++- tty-features.c | 15 ++++++++++++++- tty-term.c | 3 ++- window-client.c | 15 ++++++++------- 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/tmux.1 b/tmux.1 index 1d2f74a0a..0b6c48c40 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tmux.1,v 1.1185 2026/10/13 10:21:58 nicm Exp $ +.\" $OpenBSD: tmux.1,v 1.1186 2026/10/14 12:33:46 nicm Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -14,7 +14,7 @@ .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: October 13 2026 $ +.Dd $Mdocdate: October 14 2026 $ .Dt TMUX 1 .Os .Sh NAME @@ -5194,6 +5194,8 @@ Supports extended keys. Supports focus reporting. .It hyperlinks Supports OSC 8 hyperlinks. +.It imagescroll +Moves image content with scroll regions. .It ignorefkeys Ignore function keys from .Xr terminfo 5 @@ -8984,6 +8986,8 @@ These are set automatically if the capability is present. .It Em \&Hls Set or clear a hyperlink annotation. +.It Em \&Iscrl +Indicates that the terminal moves image content with scroll regions. .It Em \&Nobr Tell .Nm diff --git a/tmux.h b/tmux.h index f8c3e0d44..468c34297 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1461 2026/10/14 10:34:14 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1462 2026/10/14 12:33:46 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -499,6 +499,7 @@ enum tty_code_code { TTYC_IND, TTYC_INDN, TTYC_INVIS, + TTYC_ISCRL, TTYC_KCBT, TTYC_KCUB1, TTYC_KCUD1, diff --git a/tty-features.c b/tty-features.c index 1f4cd969a..3e2702f7e 100644 --- a/tty-features.c +++ b/tty-features.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tty-features.c,v 1.48 2026/10/12 12:35:03 nicm Exp $ */ +/* $OpenBSD: tty-features.c,v 1.49 2026/10/14 12:33:46 nicm Exp $ */ /* * Copyright (c) 2020 Nicholas Marriott @@ -376,6 +376,17 @@ static const struct tty_feature tty_feature_kittygraphics = { TERM_KITTYGRAPHICS }; +/* Terminal moves images with scroll regions. */ +static const char *const tty_feature_imagescroll_capabilities[] = { + "Iscrl", + NULL +}; +static const struct tty_feature tty_feature_imagescroll = { + "imagescroll", + tty_feature_imagescroll_capabilities, + 0 +}; + /* Terminal supports the OSC 9;4 progress bar. */ static const char *const tty_feature_progressbar_capabilities[] = { "Spb=\\E]9;4;%p1%d;%p2%d\\E\\\\", @@ -406,6 +417,7 @@ static const struct tty_feature *const tty_features[] = { &tty_feature_extkeys, &tty_feature_focus, &tty_feature_ignorefkeys, + &tty_feature_imagescroll, &tty_feature_kittygraphics, &tty_feature_margins, &tty_feature_mouse, @@ -645,6 +657,7 @@ tty_default_features(struct client *c, const char *name, u_int version) "extkeys," "focus," "hyperlinks," + "imagescroll," "margins," "sixel," "usstyle" diff --git a/tty-term.c b/tty-term.c index 3311701ba..7415cc552 100644 --- a/tty-term.c +++ b/tty-term.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tty-term.c,v 1.112 2026/10/12 10:39:05 nicm Exp $ */ +/* $OpenBSD: tty-term.c,v 1.113 2026/10/14 12:33:46 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -116,6 +116,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_IND] = { TTYCODE_STRING, "ind" }, [TTYC_INDN] = { TTYCODE_STRING, "indn" }, [TTYC_INVIS] = { TTYCODE_STRING, "invis" }, + [TTYC_ISCRL] = { TTYCODE_FLAG, "Iscrl" }, [TTYC_KCBT] = { TTYCODE_STRING, "kcbt" }, [TTYC_KCUB1] = { TTYCODE_STRING, "kcub1" }, [TTYC_KCUD1] = { TTYCODE_STRING, "kcud1" }, diff --git a/window-client.c b/window-client.c index 05bb2f801..c9234847f 100644 --- a/window-client.c +++ b/window-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window-client.c,v 1.50 2026/10/12 12:35:03 nicm Exp $ */ +/* $OpenBSD: window-client.c,v 1.51 2026/10/14 12:33:46 nicm Exp $ */ /* * Copyright (c) 2017 Nicholas Marriott @@ -89,19 +89,20 @@ static const char *window_client_info_lines[] = { " #[#{E:tree-mode-border-style},acs]x#[default] " WINDOW_CLIENT_FEATURE(hyperlinks) " " WINDOW_CLIENT_FEATURE(ignorefkeys) " " - WINDOW_CLIENT_FEATURE(kittygraphics) " " - WINDOW_CLIENT_FEATURE(margins), + WINDOW_CLIENT_FEATURE(imagescroll) " " + WINDOW_CLIENT_FEATURE(kittygraphics), " #[#{E:tree-mode-border-style},acs]x#[default] " + WINDOW_CLIENT_FEATURE(margins) " " WINDOW_CLIENT_FEATURE(mouse) " " WINDOW_CLIENT_FEATURE(osc7) " " - WINDOW_CLIENT_FEATURE(overline) " " - WINDOW_CLIENT_FEATURE(progressbar), + WINDOW_CLIENT_FEATURE(overline), " #[#{E:tree-mode-border-style},acs]x#[default] " + WINDOW_CLIENT_FEATURE(progressbar) " " WINDOW_CLIENT_FEATURE(rectfill) " " WINDOW_CLIENT_FEATURE(sixel) " " - WINDOW_CLIENT_FEATURE(strikethrough) " " - WINDOW_CLIENT_FEATURE(sync), + WINDOW_CLIENT_FEATURE(strikethrough), " #[#{E:tree-mode-border-style},acs]x#[default] " + WINDOW_CLIENT_FEATURE(sync) " " WINDOW_CLIENT_FEATURE(title) " " WINDOW_CLIENT_FEATURE(usstyle) " " WINDOW_CLIENT_FEATURE(utf8), From d1a68c13477b4a610c1754df105ce413ea7b6ef7 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 14 Oct 2026 12:38:18 +0000 Subject: [PATCH 08/10] Add flags to damage struct. --- screen-redraw.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 90216c84b..504b3355a 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1,4 +1,4 @@ -/* $OpenBSD: screen-redraw.c,v 1.164 2026/10/14 10:34:14 nicm Exp $ */ +/* $OpenBSD: screen-redraw.c,v 1.165 2026/10/14 12:38:18 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott @@ -218,7 +218,10 @@ struct redraw_damage { u_int sx; u_int sy; - TAILQ_ENTRY(redraw_damage) entry; + int flags; +#define REDRAW_DAMAGE_SCROLL 0x1 + + TAILQ_ENTRY(redraw_damage) entry; }; /* @@ -1134,6 +1137,7 @@ redraw_collapse_damage(struct window *w) x1 = rd->x + rd->sx; if (rd->y + rd->sy > y1) y1 = rd->y + rd->sy; + first->flags &= rd->flags; if (rd != first) { TAILQ_REMOVE(&w->damage, rd, entry); free(rd); @@ -1148,8 +1152,9 @@ redraw_collapse_damage(struct window *w) } /* Record window damage, merging nearby rectangles and limiting the count. */ -void -redraw_damage_window(struct window *w, u_int x, u_int y, u_int sx, u_int sy) +static void +redraw_apply_window_damage(struct window *w, u_int x, u_int y, u_int sx, + u_int sy, int flags) { struct redraw_damage *rd; u_int x0, y0, x1, y1, area, union_area; @@ -1182,6 +1187,7 @@ redraw_damage_window(struct window *w, u_int x, u_int y, u_int sx, u_int sy) rd->y = y0; rd->sx = x1 - x0; rd->sy = y1 - y0; + rd->flags &= flags; return; } @@ -1190,11 +1196,19 @@ redraw_damage_window(struct window *w, u_int x, u_int y, u_int sx, u_int sy) rd->y = y; rd->sx = sx; rd->sy = sy; + rd->flags = flags; TAILQ_INSERT_TAIL(&w->damage, rd, entry); if (++w->damage_count > REDRAW_DAMAGE_MAX) redraw_collapse_damage(w); } +/* Record ordinary window damage. */ +void +redraw_damage_window(struct window *w, u_int x, u_int y, u_int sx, u_int sy) +{ + redraw_apply_window_damage(w, x, y, sx, sy, 0); +} + /* Mark all cached redraw scenes as out of date. */ void redraw_invalidate_all_scenes(void) From e69cdbefefaacabce8104beecbc2d6996b7fb624 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 14 Oct 2026 12:48:50 +0000 Subject: [PATCH 09/10] Track window scrolling for damage. --- screen-redraw.c | 36 +++++++++++++++++++++++++++++++++++- tmux.h | 10 +++++++++- tty.c | 10 +++++++++- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 504b3355a..44a109fad 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1,4 +1,4 @@ -/* $OpenBSD: screen-redraw.c,v 1.165 2026/10/14 12:38:18 nicm Exp $ */ +/* $OpenBSD: screen-redraw.c,v 1.166 2026/10/14 12:48:50 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott @@ -1111,6 +1111,7 @@ redraw_free_damage(struct window *w) free(rd); } w->damage_count = 0; + w->scroll_pending = 0; } /* Collapse all pending damage for a window into one rectangle. */ @@ -1209,6 +1210,39 @@ redraw_damage_window(struct window *w, u_int x, u_int y, u_int sx, u_int sy) redraw_apply_window_damage(w, x, y, sx, sy, 0); } +/* Record damage caused by terminal scrolling. */ +void +redraw_damage_scroll(struct window *w, u_int x, u_int y, u_int sx, u_int sy) +{ + if (!w->scroll_pending) { + w->scroll_generation++; + w->scroll_pending = 1; + } + redraw_apply_window_damage(w, x, y, sx, sy, REDRAW_DAMAGE_SCROLL); +} + +/* Remember whether this client used terminal scrolling for the damage. */ +void +redraw_scroll_result(struct tty *tty, const struct tty_ctx *ctx, int failed) +{ + struct window_pane *wp = ctx->arg; + struct window *w; + + if (wp == NULL) + return; + w = wp->window; + if (!w->scroll_pending) + return; + if (tty->scroll_window != w || + tty->scroll_generation != w->scroll_generation) { + tty->scroll_window = w; + tty->scroll_generation = w->scroll_generation; + tty->scroll_failed = 0; + } + if (failed) + tty->scroll_failed = 1; +} + /* Mark all cached redraw scenes as out of date. */ void redraw_invalidate_all_scenes(void) diff --git a/tmux.h b/tmux.h index 468c34297..00293690f 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1462 2026/10/14 12:33:46 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1463 2026/10/14 12:48:50 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1479,6 +1479,8 @@ struct window { struct redraw_damages damage; u_int damage_count; + uint64_t scroll_generation; + int scroll_pending; struct menu_data *menu; u_int menu_last_px; @@ -1854,6 +1856,10 @@ struct tty { struct event key_timer; struct tty_key *key_tree; + struct window *scroll_window; + uint64_t scroll_generation; + int scroll_failed; + #ifdef ENABLE_IMAGES struct image_backend *image_backend; void *image_data; @@ -3645,6 +3651,8 @@ int redraw_client_has_window(struct client *, struct window *); void redraw_invalidate_scene(struct window *); void redraw_invalidate_all_scenes(void); void redraw_damage_window(struct window *, u_int, u_int, u_int, u_int); +void redraw_damage_scroll(struct window *, u_int, u_int, u_int, u_int); +void redraw_scroll_result(struct tty *, const struct tty_ctx *, int); void redraw_free_damage(struct window *); void redraw_client_damage(struct client *); int redraw_get_status_border_cell_type(struct redraw_span **, u_int); diff --git a/tty.c b/tty.c index e9d839853..2a4498a06 100644 --- a/tty.c +++ b/tty.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tty.c,v 1.488 2026/10/14 11:54:55 nicm Exp $ */ +/* $OpenBSD: tty.c,v 1.489 2026/10/14 12:48:50 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1681,10 +1681,12 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) !tty_term_has(tty->term, TTYC_RIN)) || ctx->sx == 1 || ctx->sy == 1) { + redraw_scroll_result(tty, ctx, 1); tty_redraw_region(tty, ctx); return; } + redraw_scroll_result(tty, ctx, 0); tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); @@ -1709,10 +1711,12 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) !tty_term_has(tty->term, TTYC_CSR) || ctx->sx == 1 || ctx->sy == 1) { + redraw_scroll_result(tty, ctx, 1); tty_redraw_region(tty, ctx); return; } + redraw_scroll_result(tty, ctx, 0); tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); @@ -1747,10 +1751,12 @@ tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) !tty_term_has(tty->term, TTYC_CSR) || ctx->sx == 1 || ctx->sy == 1) { + redraw_scroll_result(tty, ctx, 1); tty_redraw_region(tty, ctx); return; } + redraw_scroll_result(tty, ctx, 0); tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); @@ -1785,10 +1791,12 @@ tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx) !tty_term_has(tty->term, TTYC_RIN)) || ctx->sx == 1 || ctx->sy == 1) { + redraw_scroll_result(tty, ctx, 1); tty_redraw_region(tty, ctx); return; } + redraw_scroll_result(tty, ctx, 0); tty_default_attributes(tty, ctx->bg, &ctx->style_ctx); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); From 6dfed2fec08d0a96b402138e2bf03a357e62ee91 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 14 Oct 2026 12:51:00 +0000 Subject: [PATCH 10/10] Add a temporary var. --- screen-redraw.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 44a109fad..c28926a98 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1,4 +1,4 @@ -/* $OpenBSD: screen-redraw.c,v 1.166 2026/10/14 12:48:50 nicm Exp $ */ +/* $OpenBSD: screen-redraw.c,v 1.167 2026/10/14 12:51:00 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott @@ -2190,6 +2190,7 @@ void redraw_client_damage(struct client *c) { struct window *w = c->session->curw->window; + struct tty *tty = &c->tty; struct window_pane *wp; struct redraw_scene *scene; struct redraw_draw_ctx dctx; @@ -2211,8 +2212,8 @@ redraw_client_damage(struct client *c) wp->active_border_gc_set = 0; } - tty_sync_start(&c->tty); - tty_update_mode(&c->tty, c->tty.mode & ~CURSOR_MODES, NULL); + tty_sync_start(tty); + tty_update_mode(tty, c->tty.mode & ~CURSOR_MODES, NULL); TAILQ_FOREACH(rd, &w->damage, entry) { x0 = (rd->x > ox) ? rd->x : ox;