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")))