From fd990992583018f4d803fecbdbc86d84bebd7884 Mon Sep 17 00:00:00 2001 From: Amr Gharbeia Date: Mon, 18 May 2026 15:56:07 -0400 Subject: [PATCH] v0.8.0: replace inline read-raw-byte reader with cl-tty.input:read-event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- org/channel-tui-main.org | 104 +++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 58 deletions(-) diff --git a/org/channel-tui-main.org b/org/channel-tui-main.org index d0bf464..3bf2e4c 100644 --- a/org/channel-tui-main.org +++ b/org/channel-tui-main.org @@ -953,69 +953,57 @@ Returns T on success, nil on failure. Does NOT wait or retry." (if (eql ch :up) (cl-tty.select:select-prev sel) (cl-tty.select:select-next sel))) - ((member ch '(:enter 13 10)) - (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)))))) - ((let ((chr (if (characterp ch) ch - (and (integerp ch) (<= 32 ch 126) - (code-char ch))))) - (and chr (graphic-char-p chr)) - (setf (cl-tty.select:select-filter sel) - (concatenate 'string - (or (cl-tty.select:select-filter sel) "") - (string chr))))) - ((member ch '(:backspace 127 8)) + ((member ch '(:enter)) + (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)))))) + ((let ((chr (if (characterp ch) ch (code-char ch)))) + (and chr (graphic-char-p chr)) + (setf (cl-tty.select:select-filter sel) + (concatenate 'string + (or (cl-tty.select:select-filter sel) "") + (string chr))))) + ((member ch '(:backspace)) (let ((f (cl-tty.select:select-filter sel))) (when (> (length f) 0) (setf (cl-tty.select:select-filter sel) (subseq f 0 (1- f)))))))) (on-key ch)))))))) - ;; Keyboard reader via read-raw-byte (proven CSI detection) - (handler-case - (let* ((b (cl-tty.input::read-raw-byte :timeout 0.1)) - (esc-seq (and b (= b 27) - (let ((b2 (cl-tty.input::read-raw-byte :timeout 0.15))) - (when (and b2 (= b2 91)) - (let ((t2 (cl-tty.input::read-raw-byte :timeout 0.15))) - (and t2 (case t2 - (65 :up) (66 :down) - (67 :right) (68 :left) - (72 :home) (70 :end) - (otherwise :escape))))))))) - (when b - (queue-event - (list :type :key - :payload (list :code b - :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) - (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) + ;; Keyboard reader via cl-tty.input:read-event (handles CSI, SS3, UTF-8, resize) + (handler-case + (multiple-value-bind (ev resize-data) + (cl-tty.input:read-event be :timeout 0.1) + (cond + ((eq ev :resize) + (let ((new-size resize-data)) + (setq w (car new-size) h (cdr new-size)) + (setf (st :dirty) (list t t t)))) + ((cl-tty.input:key-event-p ev) + (let* ((k (cl-tty.input:key-event-key ev)) + (ctrl (cl-tty.input:key-event-ctrl ev)) + (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 + (list :type :key + :payload (list :code (or code 0) :ch ch))))))) + (error (c) + (add-msg :system (format nil "* Reader error: ~a *" c)))) + ;; 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) h (or (and (numberp h) (> h 0) h) 24)) (unless (st :dialog-stack)