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.
This commit is contained in:
@@ -42,27 +42,31 @@
|
|||||||
(raw nil :type (or string null)))
|
(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 ()
|
(defun save-terminal-state ()
|
||||||
"Save current terminal settings via stty -g. Returns a string."
|
"Save current terminal settings via stty -g. Returns a string."
|
||||||
(string-trim '(#\Newline #\Space)
|
(let ((s (string-trim '(#\Newline #\Space) (stty-run '("-g")))))
|
||||||
(with-output-to-string (s)
|
(when (zerop (length s))
|
||||||
(sb-ext:run-program "stty" '("-g") :output s :wait t))))
|
(error "stty -g failed — not running in a real terminal"))
|
||||||
|
s))
|
||||||
|
|
||||||
(defun set-raw-mode ()
|
(defun set-raw-mode ()
|
||||||
"Put terminal in raw mode via stty. Returns the saved state string."
|
"Put terminal in raw mode via stty. Returns the saved state string."
|
||||||
(let ((saved (save-terminal-state)))
|
(let ((saved (save-terminal-state)))
|
||||||
(when (zerop (length saved))
|
(stty-run '("raw" "-echo" "-isig" "-icanon" "min" "1" "time" "0"))
|
||||||
(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))
|
saved))
|
||||||
|
|
||||||
(defun restore-terminal-state (saved)
|
(defun restore-terminal-state (saved)
|
||||||
"Restore saved terminal state (a string from stty -g, or nil)."
|
"Restore saved terminal state (a string from stty -g, or nil)."
|
||||||
(when saved
|
(when (and saved (plusp (length saved)))
|
||||||
(sb-ext:run-program "stty" (list saved) :wait t :input :stdin :output t)))
|
(stty-run (list saved))))
|
||||||
|
|
||||||
(defmacro with-raw-terminal (&body body)
|
(defmacro with-raw-terminal (&body body)
|
||||||
(let ((saved (gensym "SAVED")))
|
(let ((saved (gensym "SAVED")))
|
||||||
|
|||||||
Reference in New Issue
Block a user