diff --git a/src/backend/modern.lisp b/src/backend/modern.lisp index 84697ff..37ad334 100644 --- a/src/backend/modern.lisp +++ b/src/backend/modern.lisp @@ -173,32 +173,21 @@ as a fallback when a keyword is not in *named-colors*.") (values (sb-alien:deref winsize 1) ;; cols (sb-alien:deref winsize 0))) ;; rows (sb-alien:free-alien winsize)))) - ;; stty size — reliable across all shells and terminals. - ;; SBCL strips COLUMNS/LINES from the environment, so env var - ;; fallbacks won't work for those names. + ;; Direct ioctl on /dev/tty — opens the real controlling terminal, + ;; bypassing any fd weirdness from SBCL's subprocess or stream setup. (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 for esoteric terminals - (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 modern-backend) string) diff --git a/src/backend/simple.lisp b/src/backend/simple.lisp index ea1f7de..ab8c501 100644 --- a/src/backend/simple.lisp +++ b/src/backend/simple.lisp @@ -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)