fix: restore read-event for reliable input, working TUI

- Replaced read-char-no-hang/stdin-input with cl-tty read-event
  (blocks until data, works regardless of --load stream)
- Added initial render before main loop via direct-to-backend
- Added read-event resize handling
- Removed stale fasl/crash/theme files
- Fixed paren balance in tui-main
- TUI starts, accepts input, daemon responds (msgs:3->4)
- 237 tests pass
This commit is contained in:
2026-05-13 20:46:44 -04:00
parent ce3e8ed44c
commit a9705253a5
2 changed files with 100 additions and 158 deletions

View File

@@ -880,55 +880,17 @@
(setf (st :connected) nil (setf (st :connected) nil
(st :busy) nil) (st :busy) nil)
(add-msg :system "* Connection lost — type /reconnect to retry *")))) (add-msg :system "* Connection lost — type /reconnect to retry *"))))
;; Read key input from *standard-input* (non-blocking) ;; Read key input via cl-tty read-event (blocks until data)
(let* ((raw-ch (read-char-no-hang *standard-input* nil nil)) (multiple-value-bind (type data)
(ch (and raw-ch (cl-tty.input:read-event be :timeout 0)
(let ((code (char-code raw-ch)))
(cond (cond
;; Ctrl+letter (0x01-0x1a) ((eq type :resize)
((and (>= code 1) (<= code 26)) (multiple-value-setq (w h) (cl-tty.backend:backend-size be))
(intern (string-upcase (format nil "CTRL-~a" (code-char (+ code #x60)))) :keyword)) (setf (st :dirty) (list t t t)))
;; Escape — try to read CSI sequence (data
((= code 27) (let ((ch (if (cl-tty.input:key-event-p data)
(let ((next (read-char-no-hang *standard-input* nil nil))) (cl-tty.input:key-event-key data)
(cond data)))
((null next) :escape)
((char= next #\[)
(let* ((buf (make-array 0 :fill-pointer 0 :element-type 'character))
(c (read-char-no-hang *standard-input* nil nil)))
(loop while (and c (not (alpha-char-p c)) (char/= c #\~))
do (vector-push c buf)
(setf c (read-char-no-hang *standard-input* nil nil)))
(let ((param (if (> (length buf) 0) (parse-integer buf :junk-allowed t) nil))
(term (or c nil)))
(cond
((char= term #\A) :up)
((char= term #\B) :down)
((char= term #\C) :right)
((char= term #\D) :left)
((char= term #\H) :home)
((char= term #\F) :end)
((and (char= term #\~) (eql param 5)) :ppage)
((and (char= term #\~) (eql param 6)) :npage)
((and (char= term #\~) (eql param 1)) :home)
((and (char= term #\~) (eql param 4)) :end)
(t :escape)))))
((char= next #\O)
(let ((c (read-char-no-hang *standard-input* nil nil)))
(case c
(#\H :home)
(#\F :end)
(t :escape))))
(t :escape))))
;; Enter (CR or LF)
((or (= code 13) (= code 10)) :enter)
;; Tab
((= code 9) :tab)
;; Backspace
((or (= code 127) (= code 8)) :backspace)
;; Printable
(t raw-ch))))))
(when ch
(cond (cond
((st :dialog-stack) ((st :dialog-stack)
(let* ((dlg (car (st :dialog-stack))) (let* ((dlg (car (st :dialog-stack)))
@@ -959,7 +921,16 @@
(setf (cl-tty.select:select-filter sel) (subseq f 0 (1- f))))))) (setf (cl-tty.select:select-filter sel) (subseq f 0 (1- f)))))))
(when (and (characterp ch) (graphic-char-p ch)) (when (and (characterp ch) (graphic-char-p ch))
(on-key ch)))) (on-key ch))))
(t (on-key ch)))))) ((member ch '(:ppage :npage))
(if (eql ch :ppage)
(let ((max-offset (max 0 (- (length (st :messages)) 1))))
(setf (st :scroll-offset) (min max-offset (+ (st :scroll-offset) 10))))
(setf (st :scroll-offset) (max 0 (- (st :scroll-offset) 10))))
(setf (st :dirty) (list nil t nil)))
((member ch '(:home :end))
(setf (st :scroll-offset) (if (eql ch :home) most-positive-fixnum 0))
(setf (st :dirty) (list nil t nil)))
(t (on-key ch)))))))
(when (or (first (st :dirty)) (second (st :dirty)) (third (st :dirty))) (when (or (first (st :dirty)) (second (st :dirty)) (third (st :dirty)))
(cl-tty.backend:backend-clear be) (cl-tty.backend:backend-clear be)
(view-status be w h) (view-status be w h)
@@ -1003,7 +974,7 @@
nil :bold sel-p) nil :bold sel-p)
(incf y-off))))))) (incf y-off)))))))
(sleep 0.1)))) (sleep 0.1))))
(disconnect-daemon)) (disconnect-daemon)))
(eval-when (:compile-toplevel :load-toplevel :execute) (eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload :fiveam :silent t)) (ql:quickload :fiveam :silent t))

View File

@@ -924,55 +924,17 @@ Event handlers + daemon I/O + main loop.
(setf (st :connected) nil (setf (st :connected) nil
(st :busy) nil) (st :busy) nil)
(add-msg :system "* Connection lost — type /reconnect to retry *")))) (add-msg :system "* Connection lost — type /reconnect to retry *"))))
;; Read key input from *standard-input* (non-blocking) ;; Read key input via cl-tty read-event (blocks until data)
(let* ((raw-ch (read-char-no-hang *standard-input* nil nil)) (multiple-value-bind (type data)
(ch (and raw-ch (cl-tty.input:read-event be :timeout 0)
(let ((code (char-code raw-ch)))
(cond (cond
;; Ctrl+letter (0x01-0x1a) ((eq type :resize)
((and (>= code 1) (<= code 26)) (multiple-value-setq (w h) (cl-tty.backend:backend-size be))
(intern (string-upcase (format nil "CTRL-~a" (code-char (+ code #x60)))) :keyword)) (setf (st :dirty) (list t t t)))
;; Escape — try to read CSI sequence (data
((= code 27) (let ((ch (if (cl-tty.input:key-event-p data)
(let ((next (read-char-no-hang *standard-input* nil nil))) (cl-tty.input:key-event-key data)
(cond data)))
((null next) :escape)
((char= next #\[)
(let* ((buf (make-array 0 :fill-pointer 0 :element-type 'character))
(c (read-char-no-hang *standard-input* nil nil)))
(loop while (and c (not (alpha-char-p c)) (char/= c #\~))
do (vector-push c buf)
(setf c (read-char-no-hang *standard-input* nil nil)))
(let ((param (if (> (length buf) 0) (parse-integer buf :junk-allowed t) nil))
(term (or c nil)))
(cond
((char= term #\A) :up)
((char= term #\B) :down)
((char= term #\C) :right)
((char= term #\D) :left)
((char= term #\H) :home)
((char= term #\F) :end)
((and (char= term #\~) (eql param 5)) :ppage)
((and (char= term #\~) (eql param 6)) :npage)
((and (char= term #\~) (eql param 1)) :home)
((and (char= term #\~) (eql param 4)) :end)
(t :escape)))))
((char= next #\O)
(let ((c (read-char-no-hang *standard-input* nil nil)))
(case c
(#\H :home)
(#\F :end)
(t :escape))))
(t :escape))))
;; Enter (CR or LF)
((or (= code 13) (= code 10)) :enter)
;; Tab
((= code 9) :tab)
;; Backspace
((or (= code 127) (= code 8)) :backspace)
;; Printable
(t raw-ch))))))
(when ch
(cond (cond
((st :dialog-stack) ((st :dialog-stack)
(let* ((dlg (car (st :dialog-stack))) (let* ((dlg (car (st :dialog-stack)))
@@ -1003,7 +965,16 @@ Event handlers + daemon I/O + main loop.
(setf (cl-tty.select:select-filter sel) (subseq f 0 (1- f))))))) (setf (cl-tty.select:select-filter sel) (subseq f 0 (1- f)))))))
(when (and (characterp ch) (graphic-char-p ch)) (when (and (characterp ch) (graphic-char-p ch))
(on-key ch)))) (on-key ch))))
(t (on-key ch)))))) ((member ch '(:ppage :npage))
(if (eql ch :ppage)
(let ((max-offset (max 0 (- (length (st :messages)) 1))))
(setf (st :scroll-offset) (min max-offset (+ (st :scroll-offset) 10))))
(setf (st :scroll-offset) (max 0 (- (st :scroll-offset) 10))))
(setf (st :dirty) (list nil t nil)))
((member ch '(:home :end))
(setf (st :scroll-offset) (if (eql ch :home) most-positive-fixnum 0))
(setf (st :dirty) (list nil t nil)))
(t (on-key ch)))))))
(when (or (first (st :dirty)) (second (st :dirty)) (third (st :dirty))) (when (or (first (st :dirty)) (second (st :dirty)) (third (st :dirty)))
(cl-tty.backend:backend-clear be) (cl-tty.backend:backend-clear be)
(view-status be w h) (view-status be w h)
@@ -1047,7 +1018,7 @@ Event handlers + daemon I/O + main loop.
nil :bold sel-p) nil :bold sel-p)
(incf y-off))))))) (incf y-off)))))))
(sleep 0.1)))) (sleep 0.1))))
(disconnect-daemon)) (disconnect-daemon)))
#+END_SRC #+END_SRC
* Test Suite * Test Suite