From 18843726606f5670f57cb8f9947b1ef97ea7fef0 Mon Sep 17 00:00:00 2001 From: Amr Gharbeia Date: Thu, 14 May 2026 09:02:02 -0400 Subject: [PATCH] fix: use blocking read-char via listen for reliable input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit read-char-no-hang on fd 0 streams never returns data because sb-unix:unix-simple-poll on fd 0 returns NIL in this SBCL environment. Switched to (listen tty) + (read-char tty) which blocks until a key is pressed — correct interactive TUI behavior. Also switched from (open "/dev/tty") to (sb-sys:make-fd-stream 0 :input t :buffering :none) to directly read from stdin. --- lisp/channel-tui-main.lisp | 34 +++++++++++++++++++--------------- org/channel-tui-main.org | 6 +++--- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/lisp/channel-tui-main.lisp b/lisp/channel-tui-main.lisp index 4cff6a0..807fdb2 100644 --- a/lisp/channel-tui-main.lisp +++ b/lisp/channel-tui-main.lisp @@ -864,7 +864,7 @@ (add-msg :system "* Swank unavailable *")))) (cl-tty.input:with-raw-terminal (cl-tty.backend:with-terminal (be w h) - (let ((tty (open "/dev/tty" :direction :input))) + (let ((tty (sb-sys:make-fd-stream 0 :input t :buffering :none))) ;; Initial render (cl-tty.backend:backend-clear be) (view-status be w h) @@ -887,22 +887,26 @@ (setf cl-tty.input::*terminal-resized-p* nil) (multiple-value-setq (w h) (cl-tty.backend:backend-size be)) (setf (st :dirty) (list t t t)))) - ;; Read key input from /dev/tty (non-blocking) - (let ((raw-ch (read-char-no-hang tty nil nil))) + ;; Read key input from fd 0 (blocking via listen + read-char) + ;; Note: sb-unix:unix-simple-poll on fd 0 returns NIL in this + ;; SBCL environment, so read-char-no-hang never fires. Use + ;; blocking read-char instead — the TUI loop sleeps 0.1s + ;; between renders anyway. + (let ((raw-ch (when (listen tty) (read-char tty nil nil)))) (when raw-ch (let ((code (char-code raw-ch))) - (let ((ch (cond - ((= code 13) :enter) - ((= code 10) :enter) - ((= code 27) :escape) - ((= code 9) :tab) - ((or (= code 127) (= code 8)) :backspace) - ((and (>= code 1) (<= code 26)) - (intern (string-upcase (format nil "CTRL-~a" - (code-char (+ #x60 code)))) - :keyword)) - (t raw-ch)))) - (case ch + (let ((ch (cond + ((= code 13) :enter) + ((= code 10) :enter) + ((= code 27) :escape) + ((= code 9) :tab) + ((or (= code 127) (= code 8)) :backspace) + ((and (>= code 1) (<= code 26)) + (intern (string-upcase (format nil "CTRL-~a" + (code-char (+ #x60 code)))) + :keyword)) + (t raw-ch)))) + (case ch (:CTRL-Q (setf (st :running) nil)) (:CTRL-P (command-palette-show-commands)) (:CTRL-B (setf (st :sidebar-visible) (not (st :sidebar-visible))) diff --git a/org/channel-tui-main.org b/org/channel-tui-main.org index 995b967..0e8a3b3 100644 --- a/org/channel-tui-main.org +++ b/org/channel-tui-main.org @@ -908,7 +908,7 @@ Event handlers + daemon I/O + main loop. (add-msg :system "* Swank unavailable *")))) (cl-tty.input:with-raw-terminal (cl-tty.backend:with-terminal (be w h) - (let ((tty (open "/dev/tty" :direction :input))) + (let ((tty (sb-sys:make-fd-stream 0 :input t :buffering :none))) ;; Initial render (cl-tty.backend:backend-clear be) (view-status be w h) @@ -931,8 +931,8 @@ Event handlers + daemon I/O + main loop. (setf cl-tty.input::*terminal-resized-p* nil) (multiple-value-setq (w h) (cl-tty.backend:backend-size be)) (setf (st :dirty) (list t t t)))) - ;; Read key input from /dev/tty (non-blocking) - (let ((raw-ch (read-char-no-hang tty nil nil))) + ;; Read key input from fd 0 (blocking via listen + read-char) + (let ((raw-ch (when (listen tty) (read-char tty nil nil)))) (when raw-ch (let ((code (char-code raw-ch))) (let ((ch (cond