v0.8.0: replace inline read-raw-byte reader with cl-tty.input:read-event

The old inline reader only handled basic CSI sequences (up/down/left/right/
home/end) and treated everything else as Escape. cl-tty.input:read-event
handles CSI sequences with modifiers, SS3 function keys, kitty keyboard
protocol (disambiguate escape codes), UTF-8 input, and terminal resize.

Key-event structs are converted back to the keyword/integer format that
the existing case ch dispatch and on-key expect:
  - Ctrl+letter → :CTRL-X keyword
  - :page-up/:page-down → :ppage/:npage
  - Single-char keywords → printable character via code-char
  - Everything else → pass keyword through

Removed the separate resize check block since read-event handles it.
This commit is contained in:
2026-05-18 15:56:07 -04:00
parent d157a837a9
commit fd99099258

View File

@@ -953,7 +953,7 @@ Returns T on success, nil on failure. Does NOT wait or retry."
(if (eql ch :up) (if (eql ch :up)
(cl-tty.select:select-prev sel) (cl-tty.select:select-prev sel)
(cl-tty.select:select-next sel))) (cl-tty.select:select-next sel)))
((member ch '(:enter 13 10)) ((member ch '(:enter))
(let* ((filtered (cl-tty.select:select-filtered-options sel)) (let* ((filtered (cl-tty.select:select-filtered-options sel))
(idx (cl-tty.select:select-selected-index sel)) (idx (cl-tty.select:select-selected-index sel))
(item (when (< idx (length filtered)) (item (when (< idx (length filtered))
@@ -961,60 +961,48 @@ Returns T on success, nil on failure. Does NOT wait or retry."
(when item (when item
(let ((cb (cl-tty.select:select-on-select sel))) (let ((cb (cl-tty.select:select-on-select sel)))
(when cb (funcall cb item)))))) (when cb (funcall cb item))))))
((let ((chr (if (characterp ch) ch ((let ((chr (if (characterp ch) ch (code-char ch))))
(and (integerp ch) (<= 32 ch 126)
(code-char ch)))))
(and chr (graphic-char-p chr)) (and chr (graphic-char-p chr))
(setf (cl-tty.select:select-filter sel) (setf (cl-tty.select:select-filter sel)
(concatenate 'string (concatenate 'string
(or (cl-tty.select:select-filter sel) "") (or (cl-tty.select:select-filter sel) "")
(string chr))))) (string chr)))))
((member ch '(:backspace 127 8)) ((member ch '(:backspace))
(let ((f (cl-tty.select:select-filter sel))) (let ((f (cl-tty.select:select-filter sel)))
(when (> (length f) 0) (when (> (length f) 0)
(setf (cl-tty.select:select-filter sel) (setf (cl-tty.select:select-filter sel)
(subseq f 0 (1- f)))))))) (subseq f 0 (1- f))))))))
(on-key ch)))))))) (on-key ch))))))))
;; Keyboard reader via read-raw-byte (proven CSI detection) ;; Keyboard reader via cl-tty.input:read-event (handles CSI, SS3, UTF-8, resize)
(handler-case (handler-case
(let* ((b (cl-tty.input::read-raw-byte :timeout 0.1)) (multiple-value-bind (ev resize-data)
(esc-seq (and b (= b 27) (cl-tty.input:read-event be :timeout 0.1)
(let ((b2 (cl-tty.input::read-raw-byte :timeout 0.15))) (cond
(when (and b2 (= b2 91)) ((eq ev :resize)
(let ((t2 (cl-tty.input::read-raw-byte :timeout 0.15))) (let ((new-size resize-data))
(and t2 (case t2 (setq w (car new-size) h (cdr new-size))
(65 :up) (66 :down) (setf (st :dirty) (list t t t))))
(67 :right) (68 :left) ((cl-tty.input:key-event-p ev)
(72 :home) (70 :end) (let* ((k (cl-tty.input:key-event-key ev))
(otherwise :escape))))))))) (ctrl (cl-tty.input:key-event-ctrl ev))
(when b (code (cl-tty.input:key-event-code ev))
(ch (cond
;; Ctrl+letter → :CTRL-X keyword (compatible with case dispatch)
(ctrl (let ((c (char (symbol-name k) 0)))
(intern (string-upcase (format nil "CTRL-~a" c)) :keyword)))
;; PageUp/PageDown → :ppage/:npage
((eq k :page-up) :ppage)
((eq k :page-down) :npage)
;; Single-char keyword → printable character
((and (keywordp k) (= (length (symbol-name k)) 1))
(code-char code))
;; Everything else → pass keyword through
(t k))))
(queue-event (queue-event
(list :type :key (list :type :key
:payload (list :code b :payload (list :code (or code 0) :ch ch)))))))
:ch (or esc-seq
(cond
((= b 13) :enter)
((= b 10) :enter)
((= b 27) :escape)
((= b 9) :tab)
((or (= b 127) (= b 8)) :backspace)
((and (>= b 1) (<= b 26))
(intern
(string-upcase
(format nil "CTRL-~a"
(code-char (+ #x60 b))))
:keyword))
(t b))))))))
(error (c) (error (c)
(add-msg :system (format nil "* Reader error: ~a *" c)))) (add-msg :system (format nil "* Reader error: ~a *" c))))
;; Check for terminal resize (SIGWINCH sets this flag)
(when (boundp 'cl-tty.input::*terminal-resized-p*)
(when cl-tty.input::*terminal-resized-p*
(setf cl-tty.input::*terminal-resized-p* nil)
(multiple-value-setq (w h) (cl-tty.backend:backend-size be))
(setq w (or (and (numberp w) (> w 0) w) 80)
h (or (and (numberp h) (> h 0) h) 24))
(setf (st :dirty) (list t t t))))
;; Guard w and h before render (resize or other code may have set them to nil) ;; Guard w and h before render (resize or other code may have set them to nil)
(setq w (or (and (numberp w) (> w 0) w) 80) (setq w (or (and (numberp w) (> w 0) w) 80)
h (or (and (numberp h) (> h 0) h) 24)) h (or (and (numberp h) (> h 0) h) 24))