Recalculate window sizes when the terminal reports its pixel size.

The reply to CSI 14t updated the client's cell size but not the windows,
which kept the default 16x32 cell size until some other event (a resize, or
any client connecting or exiting) triggered recalculate_sizes(). Images
parsed in the meantime were sized for the wrong cell and drawn at the wrong
size. Recalculate when the reply arrives, and extend the reply test to check
that the window follows the client without any other event.
This commit is contained in:
Michael Grant
2026-10-14 14:11:55 +02:00
parent 7e4dc561ca
commit 82d8c67504
2 changed files with 22 additions and 1 deletions

View File

@@ -12,6 +12,7 @@ TERM=screen
python3 - "$TEST_TMUX" <<'PY' python3 - "$TEST_TMUX" <<'PY'
import fcntl import fcntl
import os import os
import re
import select import select
import struct import struct
import subprocess import subprocess
@@ -94,6 +95,13 @@ for name, report in reports:
run("new-session", "-d", "-s", "replies", "-x", "80", run("new-session", "-d", "-s", "replies", "-x", "80",
"-y", "24", "stty raw -echo; exec cat -v") "-y", "24", "stty raw -echo; exec cat -v")
run("set-option", "-g", "status", "off") run("set-option", "-g", "status", "off")
if name == "pixels":
# Draw the window cell size so it can be read from the
# client's output without running another command, since
# each command's client recalculates the window sizes.
run("set-option", "-g", "status", "on")
run("set-option", "-g", "status-format[0]",
"W=#{window_cell_width}x#{window_cell_height}")
run("set-option", "-g", "assume-paste-time", "0") run("set-option", "-g", "assume-paste-time", "0")
if delivery == "expired": if delivery == "expired":
run("set-option", "-s", "escape-time", "100") run("set-option", "-s", "escape-time", "100")
@@ -128,6 +136,7 @@ for name, report in reports:
chunks = [wire[:-1], wire[-1:]] chunks = [wire[:-1], wire[-1:]]
else: else:
chunks = [wire[i:i+1] for i in range(len(wire))] chunks = [wire[i:i+1] for i in range(len(wire))]
output = b""
for part in chunks[:-1]: for part in chunks[:-1]:
os.write(fd, part) os.write(fd, part)
time.sleep(.01) time.sleep(.01)
@@ -151,12 +160,21 @@ for name, report in reports:
# A trailing key confirms that the entire reply has been # A trailing key confirms that the entire reply has been
# processed, including cases with no extra Escape. # processed, including cases with no extra Escape.
os.write(fd, chunks[-1] + b"Z") os.write(fd, chunks[-1] + b"Z")
if name == "pixels":
# With no command run since the reply, the window must
# have followed the client's new cell size by itself.
time.sleep(.3)
while select.select([fd], [], [], .2)[0]:
output += os.read(fd, 65536)
drawn = re.findall(rb"W=(\d+x\d+)", output)
assert drawn and drawn[-1] == b"10x20", (
context, "window cell size", drawn)
deadline = time.monotonic() + 5 deadline = time.monotonic() + 5
while run("capture-pane", "-p") != expected: while run("capture-pane", "-p") != expected:
assert time.monotonic() < deadline, (context, assert time.monotonic() < deadline, (context,
"reply leaked or input stalled", run("capture-pane", "-p")) "reply leaked or input stalled", run("capture-pane", "-p"))
if select.select([fd], [], [], .01)[0]: if select.select([fd], [], [], .01)[0]:
os.read(fd, 65536) output += os.read(fd, 65536)
actual = state() actual = state()
if delivery in ("invalid", "expired"): if delivery in ("invalid", "expired"):
assert actual == baseline, (context, "unexpected effect", actual) assert actual == baseline, (context, "unexpected effect", actual)

View File

@@ -1927,6 +1927,9 @@ tty_keys_winsz(struct tty *tty, const char *buf, size_t len, size_t *size,
tty_set_size(tty, tty->sx, tty->sy, char_x, char_y); tty_set_size(tty, tty->sx, tty->sy, char_x, char_y);
tty_invalidate(tty); tty_invalidate(tty);
/* Windows take their cell size from the clients. */
recalculate_sizes();
tty->flags &= ~TTY_WINSIZEQUERY; tty->flags &= ~TTY_WINSIZEQUERY;
return (0); return (0);
} }