(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)))))