Files
cl-tty/src/backend/classes.lisp
Amr Gharbeia b80bd77d84 fix: remove CSI 18t terminal query (read-char-no-hang on fd 0 never returns)
The %query-terminal-size function sent \033[18t and tried to read
the response via read-char-no-hang on fd 0, which always returns nil
in this SBCL environment. The response leaked into user input,
displaying garbled CSI sequences. Rely on ioctl only.
2026-05-14 09:32:25 -04:00

110 lines
3.4 KiB
Common Lisp

(in-package :cl-tty.backend)
(defclass backend () ())
(defgeneric initialize-backend (backend)
(:method ((b backend)) b))
(defgeneric shutdown-backend (backend)
(:method ((b backend)) (values)))
(defgeneric backend-size (backend)
(:method ((b backend))
(values 80 24)))
(defgeneric backend-write (backend string))
(defgeneric backend-clear (backend)
(:method ((b backend))
(backend-write b (format nil "~C[2J~C[H" #\Esc #\Esc))))
(defgeneric draw-text (backend x y string fg bg &key
bold italic underline reverse dim blink
&allow-other-keys))
(defgeneric draw-border (backend x y width height
&key style fg bg title title-align))
(defgeneric draw-rect (backend x y width height &key bg))
(defgeneric draw-link (backend x y string url &key fg bg))
(defgeneric draw-ellipsis (backend x y width &key fg bg))
(defgeneric cursor-move (backend x y)
(:method ((b backend) x y) (declare (ignore x y)) (values)))
(defgeneric cursor-hide (backend)
(:method ((b backend)) (values)))
(defgeneric cursor-show (backend)
(:method ((b backend)) (values)))
(defgeneric cursor-style (backend shape &key blink)
(:method ((b backend) shape &key blink) (values)))
(defgeneric begin-sync (backend)
(:method ((b backend)) (values)))
(defgeneric end-sync (backend)
(:method ((b backend)) (values)))
(defgeneric read-event (backend &key timeout)
(:method ((b backend) &key timeout) (values nil nil)))
(defgeneric enable-mouse (backend)
(:method ((b backend)) (values)))
(defgeneric enable-bracketed-paste (backend)
(:method ((b backend)) (values)))
(defgeneric capable-p (backend feature)
(:method ((b backend) feature)
(declare (ignore feature))
nil))
(in-package :cl-tty.backend)
(defgeneric suspend-backend (backend)
(:documentation "Temporarily suspend the backend, restoring terminal to normal state.
Called before SIGTSTP or similar suspension. Application should redraw after resume.")
(:method ((b backend)) (values)))
(defgeneric resume-backend (backend)
(:documentation "Re-initialize the backend after suspension.
Called after SIGCONT or similar resume. Re-enables raw mode and backend features.")
(:method ((b backend)) (values)))
(in-package :cl-tty.backend)
(defmacro with-terminal ((backend-var &optional cols-var rows-var)
&body body)
"Execute BODY with a fully initialized terminal backend.
DETECT-BACKEND, INITIALIZE-BACKEND, and SHUTDOWN-BACKEND are called
automatically. The backend instance is bound to BACKEND-VAR. If
COLS-VAR and ROWS-VAR are provided, they are bound to the terminal
dimensions at startup.
The caller should wrap this in SB-POSIX:WITH-RAW-TERMINAL (or
equivalent) if raw-mode input handling is needed.
Example:
(with-terminal (be cols rows)
(loop for ev = (read-event be :timeout 0.1)
while ev
do (format t \"~A~%\" ev))))"
(let ((be-sym (gensym "BE"))
(c-sym (gensym "COLS"))
(r-sym (gensym "ROWS")))
`(let* ((,be-sym (detect-backend))
,@(when cols-var `((,c-sym (nth-value 0 (backend-size ,be-sym)))))
,@(when rows-var `((,r-sym (nth-value 1 (backend-size ,be-sym))))))
(initialize-backend ,be-sym)
(unwind-protect
(let ((,backend-var ,be-sym)
,@(when cols-var `((,cols-var ,c-sym)))
,@(when rows-var `((,rows-var ,r-sym))))
,@body)
(shutdown-backend ,be-sym)))))