fix: prefer env vars over ioctl when ioctl returns 80x24

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).
This commit is contained in:
2026-05-14 13:02:09 -04:00
parent 5c8a253171
commit 0e0151664e
2 changed files with 51 additions and 42 deletions

View File

@@ -164,29 +164,34 @@ as a fallback when a keyword is not in *named-colors*.")
(values)) (values))
(defmethod backend-size ((b modern-backend)) (defmethod backend-size ((b modern-backend))
(or (ignore-errors (multiple-value-bind (w h)
(let* ((+tiocgwinsz+ 21523) ; 0x5413 on Linux (or (ignore-errors
(winsize (sb-alien:make-alien sb-alien:unsigned-short 4))) (let* ((+tiocgwinsz+ 21523) ; 0x5413 on Linux
(unwind-protect (winsize (sb-alien:make-alien sb-alien:unsigned-short 4)))
(progn (unwind-protect
(sb-unix:unix-ioctl (sb-sys:fd-stream-fd (backend-output-stream b)) (progn
+tiocgwinsz+ (sb-unix:unix-ioctl (sb-sys:fd-stream-fd (backend-output-stream b))
(sb-alien:alien-sap winsize)) +tiocgwinsz+
(sb-alien:alien-sap winsize))
(values (sb-alien:deref winsize 1) ;; cols (values (sb-alien:deref winsize 1) ;; cols
(sb-alien:deref winsize 0))) ;; rows (sb-alien:deref winsize 0))) ;; rows
(sb-alien:free-alien winsize)))) (sb-alien:free-alien winsize))))
;; $COLUMNS / $LINES env vars — set by every POSIX shell. (values 0 0))
;; Catches cases where ioctl on stdout's fd disagrees with ;; If ioctl returned a plausible size (not the 80x24 default),
;; the real terminal size (e.g. stdout redirected or a ;; trust it — it will be correct at runtime after SIGWINCH.
;; file descriptor mismatch). (if (and (> w 80) (> h 24))
(ignore-errors (values w h)
(let ((cols (parse-integer (sb-ext:posix-getenv "COLUMNS") ;; ioctl returned 80x24 or less — suspicious. Try $COLUMNS/
:junk-allowed t)) ;; $LINES from the shell, which reflect the true size at
(rows (parse-integer (sb-ext:posix-getenv "LINES") ;; process start even when ioctl on stdout's fd disagrees.
:junk-allowed t))) (or (ignore-errors
(when (and cols rows (> cols 0) (> rows 0)) (let ((cols (parse-integer (sb-ext:posix-getenv "COLUMNS")
(values cols rows)))) :junk-allowed t))
(values 80 24))) (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) (defmethod backend-write ((b modern-backend) string)
(let ((stream (backend-output-stream b))) (let ((stream (backend-output-stream b)))

View File

@@ -27,26 +27,30 @@
(values)) (values))
(defmethod backend-size ((b simple-backend)) (defmethod backend-size ((b simple-backend))
(or (ignore-errors (multiple-value-bind (w h)
(let* ((+tiocgwinsz+ 21523) (or (ignore-errors
(winsize (sb-alien:make-alien sb-alien:unsigned-short 4))) (let* ((+tiocgwinsz+ 21523)
(unwind-protect (winsize (sb-alien:make-alien sb-alien:unsigned-short 4)))
(progn (unwind-protect
(sb-unix:unix-ioctl (sb-sys:fd-stream-fd (progn
(backend-output-stream b)) (sb-unix:unix-ioctl (sb-sys:fd-stream-fd
+tiocgwinsz+ (backend-output-stream b))
(sb-alien:alien-sap winsize)) +tiocgwinsz+
(values (sb-alien:deref winsize 1) (sb-alien:alien-sap winsize))
(sb-alien:deref winsize 0))) (values (sb-alien:deref winsize 1)
(sb-alien:free-alien winsize)))) (sb-alien:deref winsize 0)))
(ignore-errors (sb-alien:free-alien winsize))))
(let ((cols (parse-integer (sb-ext:posix-getenv "COLUMNS") (values 0 0))
:junk-allowed t)) (if (and (> w 80) (> h 24))
(rows (parse-integer (sb-ext:posix-getenv "LINES") (values w h)
:junk-allowed t))) (or (ignore-errors
(when (and cols rows (> cols 0) (> rows 0)) (let ((cols (parse-integer (sb-ext:posix-getenv "COLUMNS")
(values cols rows)))) :junk-allowed t))
(values 80 24))) (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) (defmethod backend-write ((b simple-backend) string)
(let ((stream (backend-output-stream b))) (let ((stream (backend-output-stream b)))