From 0e0151664e927192ba85a6da88af8271eb31ef81 Mon Sep 17 00:00:00 2001 From: Amr Gharbeia Date: Thu, 14 May 2026 13:02:09 -0400 Subject: [PATCH] fix: prefer env vars over ioctl when ioctl returns 80x24 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ioctl on stdout's fd can return the default 80x24 even when the terminal is much larger (fd mismatch). The new logic: 1. Try ioctl — if it returns >80x24, trust it (correct at runtime). 2. If ioctl returned 80x24 (suspicious default), try COLUMNS/LINES from the shell environment instead. 3. If both fail, return whatever ioctl gave us (80x24). This fixes initial sizing on terminals where ioctl disagrees with the real TTY size, without breaking runtime SIGWINCH resize (which always re-queries ioctl, and that is correct after resize). --- src/backend/modern.lisp | 49 +++++++++++++++++++++++------------------ src/backend/simple.lisp | 44 +++++++++++++++++++----------------- 2 files changed, 51 insertions(+), 42 deletions(-) diff --git a/src/backend/modern.lisp b/src/backend/modern.lisp index a0b8bd1..4c15fcf 100644 --- a/src/backend/modern.lisp +++ b/src/backend/modern.lisp @@ -164,29 +164,34 @@ as a fallback when a keyword is not in *named-colors*.") (values)) (defmethod backend-size ((b modern-backend)) - (or (ignore-errors - (let* ((+tiocgwinsz+ 21523) ; 0x5413 on Linux - (winsize (sb-alien:make-alien sb-alien:unsigned-short 4))) - (unwind-protect - (progn - (sb-unix:unix-ioctl (sb-sys:fd-stream-fd (backend-output-stream b)) - +tiocgwinsz+ - (sb-alien:alien-sap winsize)) + (multiple-value-bind (w h) + (or (ignore-errors + (let* ((+tiocgwinsz+ 21523) ; 0x5413 on Linux + (winsize (sb-alien:make-alien sb-alien:unsigned-short 4))) + (unwind-protect + (progn + (sb-unix:unix-ioctl (sb-sys:fd-stream-fd (backend-output-stream b)) + +tiocgwinsz+ + (sb-alien:alien-sap winsize)) (values (sb-alien:deref winsize 1) ;; cols - (sb-alien:deref winsize 0))) ;; rows - (sb-alien:free-alien winsize)))) - ;; $COLUMNS / $LINES env vars — set by every POSIX shell. - ;; Catches cases where ioctl on stdout's fd disagrees with - ;; the real terminal size (e.g. stdout redirected or a - ;; file descriptor mismatch). - (ignore-errors - (let ((cols (parse-integer (sb-ext:posix-getenv "COLUMNS") - :junk-allowed t)) - (rows (parse-integer (sb-ext:posix-getenv "LINES") - :junk-allowed t))) - (when (and cols rows (> cols 0) (> rows 0)) - (values cols rows)))) - (values 80 24))) + (sb-alien:deref winsize 0))) ;; rows + (sb-alien:free-alien winsize)))) + (values 0 0)) + ;; If ioctl returned a plausible size (not the 80x24 default), + ;; trust it — it will be correct at runtime after SIGWINCH. + (if (and (> w 80) (> h 24)) + (values w h) + ;; ioctl returned 80x24 or less — suspicious. Try $COLUMNS/ + ;; $LINES from the shell, which reflect the true size at + ;; process start even when ioctl on stdout's fd disagrees. + (or (ignore-errors + (let ((cols (parse-integer (sb-ext:posix-getenv "COLUMNS") + :junk-allowed t)) + (rows (parse-integer (sb-ext:posix-getenv "LINES") + :junk-allowed t))) + (when (and cols rows (> cols 0) (> rows 0)) + (values cols rows)))) + (values w h))))) (defmethod backend-write ((b modern-backend) string) (let ((stream (backend-output-stream b))) diff --git a/src/backend/simple.lisp b/src/backend/simple.lisp index 4722ce4..570ab9b 100644 --- a/src/backend/simple.lisp +++ b/src/backend/simple.lisp @@ -27,26 +27,30 @@ (values)) (defmethod backend-size ((b simple-backend)) - (or (ignore-errors - (let* ((+tiocgwinsz+ 21523) - (winsize (sb-alien:make-alien sb-alien:unsigned-short 4))) - (unwind-protect - (progn - (sb-unix:unix-ioctl (sb-sys:fd-stream-fd - (backend-output-stream b)) - +tiocgwinsz+ - (sb-alien:alien-sap winsize)) - (values (sb-alien:deref winsize 1) - (sb-alien:deref winsize 0))) - (sb-alien:free-alien winsize)))) - (ignore-errors - (let ((cols (parse-integer (sb-ext:posix-getenv "COLUMNS") - :junk-allowed t)) - (rows (parse-integer (sb-ext:posix-getenv "LINES") - :junk-allowed t))) - (when (and cols rows (> cols 0) (> rows 0)) - (values cols rows)))) - (values 80 24))) + (multiple-value-bind (w h) + (or (ignore-errors + (let* ((+tiocgwinsz+ 21523) + (winsize (sb-alien:make-alien sb-alien:unsigned-short 4))) + (unwind-protect + (progn + (sb-unix:unix-ioctl (sb-sys:fd-stream-fd + (backend-output-stream b)) + +tiocgwinsz+ + (sb-alien:alien-sap winsize)) + (values (sb-alien:deref winsize 1) + (sb-alien:deref winsize 0))) + (sb-alien:free-alien winsize)))) + (values 0 0)) + (if (and (> w 80) (> h 24)) + (values w h) + (or (ignore-errors + (let ((cols (parse-integer (sb-ext:posix-getenv "COLUMNS") + :junk-allowed t)) + (rows (parse-integer (sb-ext:posix-getenv "LINES") + :junk-allowed t))) + (when (and cols rows (> cols 0) (> rows 0)) + (values cols rows)))) + (values w h))))) (defmethod backend-write ((b simple-backend) string) (let ((stream (backend-output-stream b)))