fix!: eliminate cat subprocess, use direct stdin reads, fix parens

Cat subprocess (uiop:launch-program '("cat") :input :interactive) was
unreliable — the process would exit immediately in some environments,
breaking ALL keyboard input. Root cause: uiop's :input :interactive mode
opened /dev/tty which failed under specific process-group configurations.

Replace with direct read-char-no-hang on *standard-input*. The bash script
sets stty -icanon -echo -ixon before launching sbcl, so SBCL's stdin is
already in raw mode. No subprocess needed.

Also fixed pre-existing paren imbalance in tui-main (2 extra opens).
This commit is contained in:
2026-05-15 08:33:24 -04:00
parent 3661d00138
commit f56ff4849f

View File

@@ -876,8 +876,8 @@ supplied (e.g. \"/\"), pre-fill the select filter with it."
(:down (lambda (e) (declare (ignore e)) (on-key :down)))
(:escape (lambda (e) (declare (ignore e)) (on-key :escape)))))
(defvar *cat-proc* nil "Cat subprocess for keyboard input")
(defvar *tty-in* nil "Stream from cat subprocess stdout")
(defvar *cat-proc* nil "Cat subprocess for keyboard input (unused — direct stdin reads)")
(defvar *tty-in* nil "Stream from cat subprocess stdout (unused — direct stdin reads)")
(defun tui-main ()
(init-state)
@@ -899,27 +899,12 @@ supplied (e.g. \"/\"), pre-fill the select filter with it."
(error ()
(add-msg :system "* Swank unavailable *"))))
(cl-tty.backend:with-terminal (be w h)
;; Disable echo only — keep canonical mode (line input). stty raw
;; breaks read on fd 0 in this SBCL environment, but -echo alone
;; works. A cat subprocess inherits the terminal and provides
;; bytes through a pipe that SBCL reads reliably.
;; stty -icanon -echo is set by the bash script before exec sbcl.
;; A cat subprocess reads keyboard input from the terminal.
;; Global vars for cat subprocess — avoid lexical let* scope issue
(progn
(setq *cat-proc* (uiop:launch-program '("stdbuf" "-o0" "cat")
:output :stream
:input :interactive
:stderr nil)
*tty-in* (uiop:process-info-output *cat-proc*)))
(add-msg :system (format nil "* cat pid=~a *" (uiop:process-info-pid *cat-proc*)))
;; Guard against nil w/h from backend-size
(setq w (or (and (numberp w) (> w 0) w) 80)
h (or (and (numberp h) (> h 0) h) 24))
;; Log backend info and terminal dimensions
(let ((backend-type (if (typep be 'cl-tty.backend:modern-backend)
"modern" "simple")))
(add-msg :system (format nil "* ~a backend ~dx~d *" backend-type w h)))
;; stty -icanon -echo -ixon is set by the bash script.
;; We read directly from SBCL's stdin (fd 0) since the
;; terminal is in raw mode — no cat subprocess needed.
(add-msg :system (format nil "* ~a backend ~dx~d *"
(if (typep be 'cl-tty.backend:modern-backend) "modern" "simple")
w h))
;; Initial dirty all to trigger first redraw in loop
(setq w (or (and (numberp w) (> w 0) w) 80)
h (or (and (numberp h) (> h 0) h) 24))
@@ -978,7 +963,7 @@ supplied (e.g. \"/\"), pre-fill the select filter with it."
;; Keyboard reader: block on cat pipe with 0.1s timeout.
(handler-case
(sb-ext:with-timeout 0.1
(let ((raw-ch (read-char *tty-in* nil nil)))
(let ((raw-ch (read-char-no-hang *standard-input* nil nil)))
(when raw-ch
(let ((code (char-code raw-ch)))
(queue-event
@@ -1068,10 +1053,8 @@ supplied (e.g. \"/\"), pre-fill the select filter with it."
(format nil "> ~a" filter)
(theme-color :input-prompt) nil))
(cl-tty.backend:end-sync be))
(sleep 0.1))
(uiop:terminate-process *cat-proc*))
(add-msg :system (format nil "* cat ~a ended *" (uiop:process-info-pid *cat-proc*))))
(progn (disconnect-daemon))))
(sleep 0.1))
(progn (disconnect-daemon))))))
#+END_SRC
* Test Suite