fix: replace stty size with tput cols/lines in backend-size

stty size returns incomplete data when run through uiop:run-program
(the child may not have terminal access). tput is a terminfo utility
that outputs a single number per call, avoiding parsing issues.
Works reliably in any subprocess context.
This commit is contained in:
2026-05-14 14:40:11 -04:00
parent 733ba7c1b8
commit 21c7b1c2d9
2 changed files with 26 additions and 32 deletions

View File

@@ -162,25 +162,21 @@ 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 ;; stty size via subprocess — most reliable across all systems. (or ;; tput lines / tput cols via subprocess — most reliable across
;; Returns "ROWS COLS". stty size queries the terminal via ioctl, ;; all systems. Each outputs a single number. Works in any
;; not stdin, so no :input redirection is needed. ;; subprocess context, no parsing complexity.
(multiple-value-bind (cols rows) (multiple-value-bind (cols rows)
(ignore-errors (values
(let* ((out (uiop:run-program '("stty" "size") (ignore-errors
:output :string (parse-integer
:ignore-error-status t)) (string-trim '(#\newline #\space)
(parts (and out (uiop:split-string (uiop:run-program '("tput" "cols") :output :string
(string-trim '(#\newline #\space) out))))) :ignore-error-status t))))
(when parts (ignore-errors
(let ((a (parse-integer (first parts) :junk-allowed t)) (parse-integer
(b (when (cdr parts) (parse-integer (second parts) :junk-allowed t)))) (string-trim '(#\newline #\space)
(if (and a b (> a 0) (> b 0)) (uiop:run-program '("tput" "lines") :output :string
;; stty returns "ROWS COLS" :ignore-error-status t)))))
(values b a)
;; stty returned only one value — treat as cols
(when (and a (> a 0))
(values a nil)))))))
(when (and cols rows (> cols 0) (> rows 0)) (when (and cols rows (> cols 0) (> rows 0))
(values cols rows))) (values cols rows)))
;; ioctl on stdout fd — fast, correct after SIGWINCH at runtime. ;; ioctl on stdout fd — fast, correct after SIGWINCH at runtime.

View File

@@ -22,21 +22,19 @@
(values)) (values))
(defmethod backend-size ((b simple-backend)) (defmethod backend-size ((b simple-backend))
(or ;; stty size via subprocess — most reliable across all systems. (or ;; tput cols / tput lines via subprocess
(multiple-value-bind (cols rows) (multiple-value-bind (cols rows)
(ignore-errors (values
(let* ((out (uiop:run-program '("stty" "size") (ignore-errors
:output :string (parse-integer
:ignore-error-status t)) (string-trim '(#\newline #\space)
(parts (and out (uiop:split-string (uiop:run-program '("tput" "cols") :output :string
(string-trim '(#\newline #\space) out))))) :ignore-error-status t))))
(when parts (ignore-errors
(let ((a (parse-integer (first parts) :junk-allowed t)) (parse-integer
(b (when (cdr parts) (parse-integer (second parts) :junk-allowed t)))) (string-trim '(#\newline #\space)
(if (and a b (> a 0) (> b 0)) (uiop:run-program '("tput" "lines") :output :string
(values b a) ; stty returns "ROWS COLS" :ignore-error-status t)))))
(when (and a (> a 0))
(values a nil)))))))
(when (and cols rows (> cols 0) (> rows 0)) (when (and cols rows (> cols 0) (> rows 0))
(values cols rows))) (values cols rows)))
;; ioctl on stdout fd — fast, correct after SIGWINCH at runtime. ;; ioctl on stdout fd — fast, correct after SIGWINCH at runtime.