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:
@@ -880,86 +880,57 @@
|
||||
(setf (st :connected) nil
|
||||
(st :busy) nil)
|
||||
(add-msg :system "* Connection lost — type /reconnect to retry *"))))
|
||||
;; Read key input from *standard-input* (non-blocking)
|
||||
(let* ((raw-ch (read-char-no-hang *standard-input* nil nil))
|
||||
(ch (and raw-ch
|
||||
(let ((code (char-code raw-ch)))
|
||||
(cond
|
||||
;; Ctrl+letter (0x01-0x1a)
|
||||
((and (>= code 1) (<= code 26))
|
||||
(intern (string-upcase (format nil "CTRL-~a" (code-char (+ code #x60)))) :keyword))
|
||||
;; Escape — try to read CSI sequence
|
||||
((= code 27)
|
||||
(let ((next (read-char-no-hang *standard-input* nil nil)))
|
||||
(cond
|
||||
((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
|
||||
((st :dialog-stack)
|
||||
(let* ((dlg (car (st :dialog-stack)))
|
||||
(sel (cl-tty.dialog:dialog-content dlg)))
|
||||
(cond
|
||||
((eql ch :escape)
|
||||
(pop (st :dialog-stack))
|
||||
(setf (st :minibuffer-active) nil)
|
||||
(setf (st :command-palette-active) nil)
|
||||
(setf (st :dirty) (list t t nil)))
|
||||
((member ch '(:up :down))
|
||||
(if (eql ch :up) (cl-tty.select:select-prev sel)
|
||||
(cl-tty.select:select-next sel)))
|
||||
((member ch '(:enter 13 10 #\Newline #\Return))
|
||||
(let* ((filtered (cl-tty.select:select-filtered-options sel))
|
||||
(idx (cl-tty.select:select-selected-index sel))
|
||||
(item (when (< idx (length filtered))
|
||||
(third (nth idx filtered)))))
|
||||
(when item
|
||||
(let ((cb (cl-tty.select:select-on-select sel)))
|
||||
(when cb (funcall cb item))))))
|
||||
((and (characterp ch) (graphic-char-p ch))
|
||||
(setf (cl-tty.select:select-filter sel)
|
||||
(concatenate 'string (or (cl-tty.select:select-filter sel) "") (string ch))))
|
||||
((member ch '(:backspace 127 8))
|
||||
(let ((f (cl-tty.select:select-filter sel)))
|
||||
(when (> (length f) 0)
|
||||
(setf (cl-tty.select:select-filter sel) (subseq f 0 (1- f)))))))
|
||||
;; Read key input via cl-tty read-event (blocks until data)
|
||||
(multiple-value-bind (type data)
|
||||
(cl-tty.input:read-event be :timeout 0)
|
||||
(cond
|
||||
((eq type :resize)
|
||||
(multiple-value-setq (w h) (cl-tty.backend:backend-size be))
|
||||
(setf (st :dirty) (list t t t)))
|
||||
(data
|
||||
(let ((ch (if (cl-tty.input:key-event-p data)
|
||||
(cl-tty.input:key-event-key data)
|
||||
data)))
|
||||
(cond
|
||||
((st :dialog-stack)
|
||||
(let* ((dlg (car (st :dialog-stack)))
|
||||
(sel (cl-tty.dialog:dialog-content dlg)))
|
||||
(cond
|
||||
((eql ch :escape)
|
||||
(pop (st :dialog-stack))
|
||||
(setf (st :minibuffer-active) nil)
|
||||
(setf (st :command-palette-active) nil)
|
||||
(setf (st :dirty) (list t t nil)))
|
||||
((member ch '(:up :down))
|
||||
(if (eql ch :up) (cl-tty.select:select-prev sel)
|
||||
(cl-tty.select:select-next sel)))
|
||||
((member ch '(:enter 13 10 #\Newline #\Return))
|
||||
(let* ((filtered (cl-tty.select:select-filtered-options sel))
|
||||
(idx (cl-tty.select:select-selected-index sel))
|
||||
(item (when (< idx (length filtered))
|
||||
(third (nth idx filtered)))))
|
||||
(when item
|
||||
(let ((cb (cl-tty.select:select-on-select sel)))
|
||||
(when cb (funcall cb item))))))
|
||||
((and (characterp ch) (graphic-char-p ch))
|
||||
(setf (cl-tty.select:select-filter sel)
|
||||
(concatenate 'string (or (cl-tty.select:select-filter sel) "") (string ch))))
|
||||
((member ch '(:backspace 127 8))
|
||||
(let ((f (cl-tty.select:select-filter sel)))
|
||||
(when (> (length f) 0)
|
||||
(setf (cl-tty.select:select-filter sel) (subseq f 0 (1- f)))))))
|
||||
(when (and (characterp ch) (graphic-char-p 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)))
|
||||
(cl-tty.backend:backend-clear be)
|
||||
(view-status be w h)
|
||||
@@ -1003,7 +974,7 @@
|
||||
nil :bold sel-p)
|
||||
(incf y-off)))))))
|
||||
(sleep 0.1))))
|
||||
(disconnect-daemon))
|
||||
(disconnect-daemon)))
|
||||
|
||||
(eval-when (:compile-toplevel :load-toplevel :execute)
|
||||
(ql:quickload :fiveam :silent t))
|
||||
|
||||
@@ -924,86 +924,57 @@ Event handlers + daemon I/O + main loop.
|
||||
(setf (st :connected) nil
|
||||
(st :busy) nil)
|
||||
(add-msg :system "* Connection lost — type /reconnect to retry *"))))
|
||||
;; Read key input from *standard-input* (non-blocking)
|
||||
(let* ((raw-ch (read-char-no-hang *standard-input* nil nil))
|
||||
(ch (and raw-ch
|
||||
(let ((code (char-code raw-ch)))
|
||||
(cond
|
||||
;; Ctrl+letter (0x01-0x1a)
|
||||
((and (>= code 1) (<= code 26))
|
||||
(intern (string-upcase (format nil "CTRL-~a" (code-char (+ code #x60)))) :keyword))
|
||||
;; Escape — try to read CSI sequence
|
||||
((= code 27)
|
||||
(let ((next (read-char-no-hang *standard-input* nil nil)))
|
||||
(cond
|
||||
((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
|
||||
((st :dialog-stack)
|
||||
(let* ((dlg (car (st :dialog-stack)))
|
||||
(sel (cl-tty.dialog:dialog-content dlg)))
|
||||
(cond
|
||||
((eql ch :escape)
|
||||
(pop (st :dialog-stack))
|
||||
(setf (st :minibuffer-active) nil)
|
||||
(setf (st :command-palette-active) nil)
|
||||
(setf (st :dirty) (list t t nil)))
|
||||
((member ch '(:up :down))
|
||||
(if (eql ch :up) (cl-tty.select:select-prev sel)
|
||||
(cl-tty.select:select-next sel)))
|
||||
((member ch '(:enter 13 10 #\Newline #\Return))
|
||||
(let* ((filtered (cl-tty.select:select-filtered-options sel))
|
||||
(idx (cl-tty.select:select-selected-index sel))
|
||||
(item (when (< idx (length filtered))
|
||||
(third (nth idx filtered)))))
|
||||
(when item
|
||||
(let ((cb (cl-tty.select:select-on-select sel)))
|
||||
(when cb (funcall cb item))))))
|
||||
((and (characterp ch) (graphic-char-p ch))
|
||||
(setf (cl-tty.select:select-filter sel)
|
||||
(concatenate 'string (or (cl-tty.select:select-filter sel) "") (string ch))))
|
||||
((member ch '(:backspace 127 8))
|
||||
(let ((f (cl-tty.select:select-filter sel)))
|
||||
(when (> (length f) 0)
|
||||
(setf (cl-tty.select:select-filter sel) (subseq f 0 (1- f)))))))
|
||||
;; Read key input via cl-tty read-event (blocks until data)
|
||||
(multiple-value-bind (type data)
|
||||
(cl-tty.input:read-event be :timeout 0)
|
||||
(cond
|
||||
((eq type :resize)
|
||||
(multiple-value-setq (w h) (cl-tty.backend:backend-size be))
|
||||
(setf (st :dirty) (list t t t)))
|
||||
(data
|
||||
(let ((ch (if (cl-tty.input:key-event-p data)
|
||||
(cl-tty.input:key-event-key data)
|
||||
data)))
|
||||
(cond
|
||||
((st :dialog-stack)
|
||||
(let* ((dlg (car (st :dialog-stack)))
|
||||
(sel (cl-tty.dialog:dialog-content dlg)))
|
||||
(cond
|
||||
((eql ch :escape)
|
||||
(pop (st :dialog-stack))
|
||||
(setf (st :minibuffer-active) nil)
|
||||
(setf (st :command-palette-active) nil)
|
||||
(setf (st :dirty) (list t t nil)))
|
||||
((member ch '(:up :down))
|
||||
(if (eql ch :up) (cl-tty.select:select-prev sel)
|
||||
(cl-tty.select:select-next sel)))
|
||||
((member ch '(:enter 13 10 #\Newline #\Return))
|
||||
(let* ((filtered (cl-tty.select:select-filtered-options sel))
|
||||
(idx (cl-tty.select:select-selected-index sel))
|
||||
(item (when (< idx (length filtered))
|
||||
(third (nth idx filtered)))))
|
||||
(when item
|
||||
(let ((cb (cl-tty.select:select-on-select sel)))
|
||||
(when cb (funcall cb item))))))
|
||||
((and (characterp ch) (graphic-char-p ch))
|
||||
(setf (cl-tty.select:select-filter sel)
|
||||
(concatenate 'string (or (cl-tty.select:select-filter sel) "") (string ch))))
|
||||
((member ch '(:backspace 127 8))
|
||||
(let ((f (cl-tty.select:select-filter sel)))
|
||||
(when (> (length f) 0)
|
||||
(setf (cl-tty.select:select-filter sel) (subseq f 0 (1- f)))))))
|
||||
(when (and (characterp ch) (graphic-char-p 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)))
|
||||
(cl-tty.backend:backend-clear be)
|
||||
(view-status be w h)
|
||||
@@ -1047,7 +1018,7 @@ Event handlers + daemon I/O + main loop.
|
||||
nil :bold sel-p)
|
||||
(incf y-off)))))))
|
||||
(sleep 0.1))))
|
||||
(disconnect-daemon))
|
||||
(disconnect-daemon)))
|
||||
#+END_SRC
|
||||
|
||||
* Test Suite
|
||||
|
||||
Reference in New Issue
Block a user