From 2649dbeb79bf65e8ae3a389b3bf2ccbb01f36d7c Mon Sep 17 00:00:00 2001 From: Hermes Date: Tue, 12 May 2026 01:35:25 +0000 Subject: [PATCH] Replace sb-posix:termios raw mode with stty-based approach set-raw-mode now uses (stty raw -echo ...) via sb-ext:run-program instead of sb-posix:tcgetattr/tcsetattr + termios flag manipulation. The sb-posix termios API changed between SBCL versions (termios-cc accessor went from 2-arg to 1-arg), and tcgetattr fails in some container/PTY environments. Stty is available on every Unix and is independent of SBCL's sb-posix version. set-raw-mode errors if stty -g returns empty (no real terminal attached). restore-terminal-state is a no-op when called with nil. --- src/components/input.lisp | 44 ++++++++++++++------------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/src/components/input.lisp b/src/components/input.lisp index 6f3c0db..0e5ae1c 100644 --- a/src/components/input.lisp +++ b/src/components/input.lisp @@ -42,39 +42,27 @@ (raw nil :type (or string null))) ;;; --------------------------------------------------------------------------- -;;; Terminal raw mode +;;; Terminal raw mode (stty-based — portable across Unices) ;;; --------------------------------------------------------------------------- (defun save-terminal-state () - (sb-posix:tcgetattr 0)) - -(defun make-raw-termios (termios) - (flet ((clear-flag (flags mask) - (logand flags (lognot mask)))) - (setf (sb-posix:termios-iflag termios) - (clear-flag (sb-posix:termios-iflag termios) - (logior sb-posix:brkint sb-posix:ignpar - sb-posix:istrip sb-posix:inlcr - sb-posix:igncr sb-posix:icrnl - sb-posix:ixon))) - (setf (sb-posix:termios-oflag termios) - (clear-flag (sb-posix:termios-oflag termios) - sb-posix:opost)) - (setf (sb-posix:termios-lflag termios) - (clear-flag (sb-posix:termios-lflag termios) - (logior sb-posix:icanon sb-posix:echo - sb-posix:isig sb-posix:iexten))) - (let ((cc-array (sb-posix:termios-cc termios))) - (setf (aref cc-array sb-posix:vmin) 1) - (setf (aref cc-array sb-posix:vtime) 0)) - termios)) + "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)))) (defun set-raw-mode () - (let ((raw (make-raw-termios (save-terminal-state)))) - (sb-posix:tcsetattr 0 sb-posix:tcsanow raw) - raw)) + "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) + saved)) -(defun restore-terminal-state (termios) - (sb-posix:tcsetattr 0 sb-posix:tcsanow termios)) +(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))) (defmacro with-raw-terminal (&body body) (let ((saved (gensym "SAVED")))