fix: replace stty/tput fallback with direct ioctl on /dev/tty

uiop:run-program can inherit different terminal state than the
interactive shell. Opening /dev/tty directly and calling ioctl
on that fd is equivalent to what the shell's stty does, and
works regardless of SBCL's fd inheritance quirks.
This commit is contained in:
2026-05-14 13:56:16 -04:00
parent 9b472e281f
commit 37f83db35e
2 changed files with 27 additions and 48 deletions

View File

@@ -34,30 +34,20 @@
(values (sb-alien:deref winsize 1)
(sb-alien:deref winsize 0)))
(sb-alien:free-alien winsize))))
;; stty size — reliable across all shells and terminals.
;; Direct ioctl on /dev/tty — opens the real controlling terminal.
(ignore-errors
(let* ((out (uiop:run-program '("stty" "size")
:output :string
:ignore-error-status t))
(parts (and out (uiop:split-string
(string-trim '(#\newline #\space) out)))))
(when (and parts (= (length parts) 2))
(let ((rows (parse-integer (first parts) :junk-allowed t))
(cols (parse-integer (second parts) :junk-allowed t)))
(when (and rows cols (> rows 0) (> cols 0))
(values cols rows))))))
;; tput — final shell fallback
(ignore-errors
(let* ((out (uiop:run-program '("sh" "-c" "tput cols 2>/dev/null; tput lines 2>/dev/null")
:output :string
:ignore-error-status t))
(parts (and out (uiop:split-string
(string-trim '(#\newline #\space) out)))))
(when (and parts (= (length parts) 2))
(let ((cols (parse-integer (first parts) :junk-allowed t))
(rows (parse-integer (second parts) :junk-allowed t)))
(when (and rows cols (> rows 0) (> cols 0))
(values cols rows))))))
(let ((tty-fd (sb-unix:unix-open "/dev/tty" sb-unix:o-rdwr 0)))
(when tty-fd
(unwind-protect
(let* ((winsize (sb-alien:make-alien sb-alien:unsigned-short 4)))
(sb-unix:unix-ioctl tty-fd 21523 (sb-alien:alien-sap winsize))
(let ((cols (sb-alien:deref winsize 1))
(rows (sb-alien:deref winsize 0)))
(sb-alien:free-alien winsize)
(when (and (integerp cols) (integerp rows)
(> cols 0) (> rows 0))
(values cols rows))))
(sb-unix:unix-close tty-fd)))))
(values 80 24)))
(defmethod backend-write ((b simple-backend) string)