diff --git a/src/backend/modern.lisp b/src/backend/modern.lisp index db4c992..02a7441 100644 --- a/src/backend/modern.lisp +++ b/src/backend/modern.lisp @@ -162,21 +162,20 @@ as a fallback when a keyword is not in *named-colors*.") (values)) (defmethod backend-size ((b modern-backend)) - (or ;; tput lines / tput cols via subprocess — most reliable across - ;; all systems. Each outputs a single number. Works in any - ;; subprocess context, no parsing complexity. + (or ;; stty size with :input :inherit — preserves the parent's fd 0 + ;; (the terminal) so stty can query it via ioctl. (multiple-value-bind (cols rows) - (values - (ignore-errors - (parse-integer - (string-trim '(#\newline #\space) - (uiop:run-program '("tput" "cols") :output :string - :ignore-error-status t)))) - (ignore-errors - (parse-integer - (string-trim '(#\newline #\space) - (uiop:run-program '("tput" "lines") :output :string - :ignore-error-status t))))) + (ignore-errors + (let* ((out (uiop:run-program '("stty" "size") :output :string + :input :inherit + :ignore-error-status t)) + (parts (and out (uiop:split-string + (string-trim '(#\newline #\space) out))))) + (when (and parts (= (length parts) 2)) + (let ((r (parse-integer (first parts) :junk-allowed t)) + (c (parse-integer (second parts) :junk-allowed t))) + (when (and r c (> r 0) (> c 0)) + (values c r)))))) (when (and cols rows (> cols 0) (> rows 0)) (values cols rows))) ;; ioctl on stdout fd — fast, correct after SIGWINCH at runtime. diff --git a/src/backend/simple.lisp b/src/backend/simple.lisp index 3a76769..f67e65d 100644 --- a/src/backend/simple.lisp +++ b/src/backend/simple.lisp @@ -22,19 +22,19 @@ (values)) (defmethod backend-size ((b simple-backend)) - (or ;; tput cols / tput lines via subprocess + (or ;; stty size with :input :inherit — preserves parent's fd 0. (multiple-value-bind (cols rows) - (values - (ignore-errors - (parse-integer - (string-trim '(#\newline #\space) - (uiop:run-program '("tput" "cols") :output :string - :ignore-error-status t)))) - (ignore-errors - (parse-integer - (string-trim '(#\newline #\space) - (uiop:run-program '("tput" "lines") :output :string - :ignore-error-status t))))) + (ignore-errors + (let* ((out (uiop:run-program '("stty" "size") :output :string + :input :inherit + :ignore-error-status t)) + (parts (and out (uiop:split-string + (string-trim '(#\newline #\space) out))))) + (when (and parts (= (length parts) 2)) + (let ((r (parse-integer (first parts) :junk-allowed t)) + (c (parse-integer (second parts) :junk-allowed t))) + (when (and r c (> r 0) (> c 0)) + (values c r)))))) (when (and cols rows (> cols 0) (> rows 0)) (values cols rows))) ;; ioctl on stdout fd — fast, correct after SIGWINCH at runtime.