From 4b1ff3ed0fac24f5d9d4145f2ee8dd7927a61f94 Mon Sep 17 00:00:00 2001 From: Amr Gharbeia Date: Thu, 14 May 2026 14:22:27 -0400 Subject: [PATCH] fix: move stty size to first priority in backend-size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit stty size via subprocess is the most reliable method — it returns the correct 59x83 from the user's terminal. Move it before ioctl to ensure it's tried first. --- src/backend/modern.lisp | 30 +++++++++++++++--------------- src/backend/simple.lisp | 14 +++++++++++++- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/backend/modern.lisp b/src/backend/modern.lisp index a87fd30..a9a4e8f 100644 --- a/src/backend/modern.lisp +++ b/src/backend/modern.lisp @@ -162,21 +162,9 @@ 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)) - (values (sb-alien:deref winsize 1) ;; cols - (sb-alien:deref winsize 0))) ;; rows - (sb-alien:free-alien winsize)))) - ;; stty size via subprocess — reliable on every Unix, bypasses - ;; SBCL's alien/ioctl quirks. Returns "ROWS COLS". Use sh -c - ;; with explicit /dev/tty to ensure stty sees the real terminal - ;; (uiop:run-program may redirect stdin). + (or ;; stty size via subprocess — most reliable across all systems. + ;; Returns "ROWS COLS". Uses explicit /dev/tty to bypass any + ;; stdin redirection from uiop:run-program. (ignore-errors (let* ((out (uiop:run-program '("sh" "-c" "stty size < /dev/tty") :output :string @@ -188,6 +176,18 @@ as a fallback when a keyword is not in *named-colors*.") (cols (parse-integer (second parts) :junk-allowed t))) (when (and rows cols (> rows 0) (> cols 0)) (values cols rows)))))) + ;; ioctl on stdout fd — fast, correct after SIGWINCH at runtime. + (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)))) ;; Direct ioctl on /dev/tty — opens the real controlling terminal. (ignore-errors (let ((tty-fd (sb-unix:unix-open "/dev/tty" 0 0))) ; O_RDONLY diff --git a/src/backend/simple.lisp b/src/backend/simple.lisp index 52bdce1..bab5a3b 100644 --- a/src/backend/simple.lisp +++ b/src/backend/simple.lisp @@ -22,7 +22,19 @@ (values)) (defmethod backend-size ((b simple-backend)) - (or (ignore-errors + (or ;; stty size via subprocess — most reliable across all systems. + (ignore-errors + (let* ((out (uiop:run-program '("sh" "-c" "stty size < /dev/tty") + :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)))))) + (ignore-errors (let* ((+tiocgwinsz+ 21523) (winsize (sb-alien:make-alien sb-alien:unsigned-short 4))) (unwind-protect