From 0ed74278023e8e82ef2ec1ef95950d4315f395e3 Mon Sep 17 00:00:00 2001 From: Hermes Date: Tue, 12 May 2026 01:40:24 +0000 Subject: [PATCH] Raw mode via stty -F /dev/tty, explicit device path stty now operates on /dev/tty explicitly (-F flag) instead of relying on stdin inheritance. This is more reliable in SBCL's --script mode where stdin may be handled differently by run-program. Also ensures stty always targets the controlling terminal regardless of how the subprocess is spawned. --- src/components/input.lisp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/components/input.lisp b/src/components/input.lisp index 0e5ae1c..48af8eb 100644 --- a/src/components/input.lisp +++ b/src/components/input.lisp @@ -42,27 +42,31 @@ (raw nil :type (or string null))) ;;; --------------------------------------------------------------------------- -;;; Terminal raw mode (stty-based — portable across Unices) +;;; Terminal raw mode (stty on /dev/tty — portable across Unices) ;;; --------------------------------------------------------------------------- +(defun stty-run (args) + "Run stty with ARGS on the controlling terminal. Returns stdout as string." + (with-output-to-string (s) + (sb-ext:run-program "stty" (append '("-F" "/dev/tty") args) + :output s :input :stdin :wait t))) + (defun save-terminal-state () "Save current terminal settings via stty -g. Returns a string." - (string-trim '(#\Newline #\Space) - (with-output-to-string (s) - (sb-ext:run-program "stty" '("-g") :output s :wait t)))) + (let ((s (string-trim '(#\Newline #\Space) (stty-run '("-g"))))) + (when (zerop (length s)) + (error "stty -g failed — not running in a real terminal")) + s)) (defun set-raw-mode () "Put terminal in raw mode via stty. Returns the saved state string." (let ((saved (save-terminal-state))) - (when (zerop (length saved)) - (error "stty -g failed: stdout is not a terminal")) - (sb-ext:run-program "stty" '("raw" "-echo" "isig" "icanon" "min" "1" "time" "0") - :wait t :input :stdin :output t) + (stty-run '("raw" "-echo" "-isig" "-icanon" "min" "1" "time" "0")) saved)) (defun restore-terminal-state (saved) "Restore saved terminal state (a string from stty -g, or nil)." - (when saved - (sb-ext:run-program "stty" (list saved) :wait t :input :stdin :output t))) + (when (and saved (plusp (length saved))) + (stty-run (list saved)))) (defmacro with-raw-terminal (&body body) (let ((saved (gensym "SAVED")))