This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch master
in repository terminology.
View the commit online.
commit 0bf6de622e90f5eddf0cbe31d1b9f6ffc1bc1770
Author: Boris Faure <[email protected]>
AuthorDate: Sat Sep 19 15:30:26 2026 +0000
termpty: keep the alternate screen across a resize
---
src/bin/termpty.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++---
src/bin/tytest.c | 1 +
src/bin/unit_tests.h | 1 +
3 files changed, 64 insertions(+), 3 deletions(-)
diff --git a/src/bin/termpty.c b/src/bin/termpty.c
index 40527a36..09d501c8 100644
--- a/src/bin/termpty.c
+++ b/src/bin/termpty.c
@@ -1442,7 +1442,8 @@ _backlog_remove_latest_nolock(Termpty *ty)
void
termpty_resize(Termpty *ty, int new_w, int new_h)
{
- Termcell *new_screen = NULL;
+ Termcell *new_screen = NULL,
+ *old_screen2 = NULL;
int old_y = 0,
old_w = ty->w,
old_h = ty->h,
@@ -1466,10 +1467,33 @@ termpty_resize(Termpty *ty, int new_w, int new_h)
new_screen = calloc(1, sizeof(Termcell) * new_w * new_h);
if (!new_screen)
goto bad;
- free(ty->screen2);
+
+ /* The inactive screen is never rewrapped, but it must not be dropped: when
+ * the alternate buffer is on, it holds what the application is showing and
+ * curses applications only send diffs after SIGWINCH. */
+ old_screen2 = ty->screen2;
ty->screen2 = calloc(1, sizeof(Termcell) * new_w * new_h);
if (!ty->screen2)
- goto bad;
+ {
+ ty->screen2 = old_screen2;
+ goto bad;
+ }
+ if (old_screen2)
+ {
+ int copy_w = MIN(old_w, new_w),
+ copy_h = MIN(old_h, new_h),
+ y;
+
+ for (y = 0; y < copy_h; y++)
+ {
+ int src_y = (y + ty->circular_offset2) % old_h;
+
+ memcpy(&ty->screen2[y * new_w],
+ &old_screen2[src_y * old_w],
+ copy_w * sizeof(Termcell));
+ }
+ }
+ free(old_screen2);
new_si.screen = new_screen;
new_si.w = new_w;
@@ -2134,6 +2158,41 @@ tytest_sync_resize(void)
return 0;
}
+/* A resize must keep the alternate screen's cells that still fit: curses
+ * applications only redraw what they think changed after SIGWINCH, so
+ * dropping them leaves the screen blank (issue #212). */
+int
+tytest_altscreen_resize_keeps_content(void)
+{
+ Termpty ty;
+
+ _ty_test_init(&ty, 80, 24);
+
+ _ty_feed(&ty, "\x1b[1;1Hnormal");
+ _ty_feed(&ty, "\x1b[?1049h");
+ assert(ty.altbuf);
+ _ty_feed(&ty, "\x1b[3;1Halpha");
+ assert(_ty_cell_cp(&ty, 1, 2) == 'l');
+
+ /* Shrinking keeps what still fits. */
+ termpty_resize(&ty, 70, 20);
+ assert(ty.altbuf);
+ assert(_ty_cell_cp(&ty, 1, 2) == 'l');
+ assert(_ty_cell_cp(&ty, 4, 2) == 'a');
+
+ /* Growing keeps it too, on the same row. */
+ termpty_resize(&ty, 100, 30);
+ assert(_ty_cell_cp(&ty, 1, 2) == 'l');
+
+ /* The normal screen went through the rewrap path meanwhile. */
+ _ty_feed(&ty, "\x1b[?1049l");
+ assert(!ty.altbuf);
+ assert(_ty_cell_cp(&ty, 1, 0) == 'o');
+
+ _ty_test_shutdown(&ty);
+ return 0;
+}
+
/* Test 5: Soft reset during sync.
* BSU + writes + DECSTR (\e[!p). Snapshot freed. */
int
diff --git a/src/bin/tytest.c b/src/bin/tytest.c
index 9365c0b3..94400af5 100644
--- a/src/bin/tytest.c
+++ b/src/bin/tytest.c
@@ -51,6 +51,7 @@ static struct {
{ "sync_watchdog_teardown", tytest_sync_watchdog_teardown},
{ "sync_nested", tytest_sync_nested},
{ "sync_resize", tytest_sync_resize},
+ { "altscreen_resize_keeps_content", tytest_altscreen_resize_keeps_content},
{ "sync_soft_reset", tytest_sync_soft_reset},
{ "sync_change_cb_coalesced", tytest_sync_change_cb_coalesced},
{ "sync_change_cb_watchdog", tytest_sync_change_cb_watchdog},
diff --git a/src/bin/unit_tests.h b/src/bin/unit_tests.h
index 4d07e422..de5ac432 100644
--- a/src/bin/unit_tests.h
+++ b/src/bin/unit_tests.h
@@ -25,6 +25,7 @@ int tytest_sync_frame_coherence(void);
int tytest_sync_watchdog_teardown(void);
int tytest_sync_nested(void);
int tytest_sync_resize(void);
+int tytest_altscreen_resize_keeps_content(void);
int tytest_sync_soft_reset(void);
int tytest_sync_change_cb_coalesced(void);
int tytest_sync_change_cb_watchdog(void);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.