diff --git a/lisp/channel-tui-main.lisp b/lisp/channel-tui-main.lisp index e21edbf..a4de9fc 100644 --- a/lisp/channel-tui-main.lisp +++ b/lisp/channel-tui-main.lisp @@ -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)) diff --git a/org/channel-tui-main.org b/org/channel-tui-main.org index 938c5d2..fdea05d 100644 --- a/org/channel-tui-main.org +++ b/org/channel-tui-main.org @@ -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