diff options
| author | user <user@clank> | 2026-08-14 23:13:56 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-14 23:13:56 +0200 |
| commit | 4b380e9b4cbd24dca99bef4991541aa1aece847f (patch) | |
| tree | fcd41dfdf23f17027523336dbe448ee683e72c16 /src/client.c | |
| parent | vendor: keep kitty images across a screen clear (diff) | |
| download | slosh-4b380e9b4cbd24dca99bef4991541aa1aece847f.tar.gz slosh-4b380e9b4cbd24dca99bef4991541aa1aece847f.tar.xz slosh-4b380e9b4cbd24dca99bef4991541aa1aece847f.zip | |
sl0ppty: carry the terminal's cell size, so images can have a size
"Kitty graphics pass through" was true of the tests and not of anything else.
Every test transmitted and placed in one command with explicit c=/r=; real
programs upload once with `a=t` and place per frame with `a=p`, saying
nothing about cells, and that path was broken from end to end.
A placement that does not say how many cells it covers is sized from the
image's pixels and the cell size. We never had a cell size:
- pty_spawn and pty_resize left ws_xpixel/ws_ypixel at zero, so a program
asking its own tty how big a cell is got nothing and fell back to guessing;
- ghostty_terminal_resize was called with 0, 0 for the cell size, so lib-vt
computed zero cells for such a placement -- and a placement covering zero
cells is skipped, which on screen is an image that simply is not there.
So the size now runs the whole way through. The client reads it from
TIOCGWINSZ (re-read on every resize, because a window can move to a monitor
with different metrics), MSG_HELLO and MSG_RESIZE carry it -- a 4-byte
payload still means "I did not say", so an older client still attaches -- the
app hands it to every pane in every tab, and each pane gives it to both its
pty and its terminal.
Where nobody knows, the default is 8x16 rather than 0. That is the whole
lesson of this bug: a plausible cell size means an image appears at roughly
the right size, and a zero means it does not appear at all.
`{"cmd":"resize"}` takes optional cell_w/cell_h, so a script can be a client
that knows its own metrics, and so the tests can be one.
The tests now use the protocol the way programs use it, including a check
that a program can read real pixel dimensions out of its own pty -- the thing
that was silently zero. The remaining half of the fix is one layer down, in
vendor/patches/0001.
Diffstat (limited to 'src/client.c')
| -rw-r--r-- | src/client.c | 32 |
1 files changed, 23 insertions, 9 deletions
diff --git a/src/client.c b/src/client.c index a3ad73d..2c22af9 100644 --- a/src/client.c +++ b/src/client.c @@ -41,20 +41,32 @@ static void restore(void) { (void)r; } -static void term_size(uint16_t *cols, uint16_t *rows) { +/* The terminal's size, in cells and — when it will say — in pixels per cell. + * Most terminals fill in the pixel fields; the ones that do not report zero, + * which is passed on as "I do not know" rather than as a size. */ +static void term_size(uint16_t *cols, uint16_t *rows, uint16_t *cell_w, + uint16_t *cell_h) { struct winsize ws; + *cell_w = *cell_h = 0; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col && ws.ws_row) { *cols = ws.ws_col; *rows = ws.ws_row; + if (ws.ws_xpixel && ws.ws_ypixel) { + *cell_w = (uint16_t)(ws.ws_xpixel / ws.ws_col); + *cell_h = (uint16_t)(ws.ws_ypixel / ws.ws_row); + } } else { *cols = 80; *rows = 24; } } -static void send_size(int fd, uint8_t type, uint16_t cols, uint16_t rows) { - uint8_t b[4] = {(uint8_t)(cols >> 8), (uint8_t)cols, (uint8_t)(rows >> 8), - (uint8_t)rows}; +static void send_size(int fd, uint8_t type, uint16_t cols, uint16_t rows, + uint16_t cell_w, uint16_t cell_h) { + uint8_t b[8] = {(uint8_t)(cols >> 8), (uint8_t)cols, + (uint8_t)(rows >> 8), (uint8_t)rows, + (uint8_t)(cell_w >> 8), (uint8_t)cell_w, + (uint8_t)(cell_h >> 8), (uint8_t)cell_h}; msg_send(fd, type, b, sizeof b); } @@ -71,8 +83,8 @@ static void write_all(int fd, const void *buf, size_t len) { } int client_run(int fd) { - uint16_t cols, rows; - term_size(&cols, &rows); + uint16_t cols, rows, cell_w, cell_h; + term_size(&cols, &rows, &cell_w, &cell_h); if (tcgetattr(STDIN_FILENO, &g_saved) != 0) { fprintf(stderr, "sl0ppty: not a terminal\n"); @@ -93,7 +105,7 @@ int client_run(int fd) { sigaction(SIGHUP, &sa, NULL); signal(SIGPIPE, SIG_IGN); - send_size(fd, MSG_HELLO, cols, rows); + send_size(fd, MSG_HELLO, cols, rows, cell_w, cell_h); msg_reader_t reader; msg_reader_init(&reader); @@ -108,8 +120,10 @@ int client_run(int fd) { if (g_winch) { g_winch = 0; - term_size(&cols, &rows); - send_size(fd, MSG_RESIZE, cols, rows); + /* A resize can also be a move to another monitor, so the cell size is + * re-read rather than remembered from the first frame. */ + term_size(&cols, &rows, &cell_w, &cell_h); + send_size(fd, MSG_RESIZE, cols, rows, cell_w, cell_h); } if (n < 0) { if (errno == EINTR) continue; |
