literate: add with-terminal, suspend-backend, resume-backend to org source

with-terminal macro was only in tangled .lisp (not .org). suspend-backend
and resume-backend generics + simple-backend methods + tests were also
in hand-edited .lisp only. All three added to org/backend-protocol.org
with proper prose, following the literate programming discipline.

Also added suspend/resume assertions to simple-backend-lifecycle test suite.
This commit is contained in:
2026-05-13 13:14:24 -04:00
parent c30917056c
commit 66e86734cb
4 changed files with 146 additions and 48 deletions

View File

@@ -8,47 +8,6 @@
(defgeneric shutdown-backend (backend)
(:method ((b backend)) (values)))
(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)))
(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)))))
(defgeneric backend-size (backend)
(:method ((b backend))
(values 80 24)))
@@ -103,3 +62,48 @@ Example:
(: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)))))

View File

@@ -19,9 +19,9 @@
;; Queries
#:capable-p
;; Constructors
#:make-simple-backend
#:with-terminal
;; Modern backend
#:make-simple-backend
#:with-terminal
;; Modern backend
#:modern-backend #:make-modern-backend
;; Detection
#:detect-backend #:*detected-backend*

View File

@@ -24,6 +24,8 @@
(is (typep b 'simple-backend))
(initialize-backend b)
(is-false (capable-p b :truecolor) "simple backend has no truecolor")
(is (null (multiple-value-list (suspend-backend b))))
(is (null (multiple-value-list (resume-backend b))))
(shutdown-backend b)))
(test simple-backend-draw-text
@@ -103,8 +105,6 @@
(is (null (multiple-value-list (cursor-style b :block))))
(is (null (multiple-value-list (begin-sync b))))
(is (null (multiple-value-list (end-sync b))))
(is (null (multiple-value-list (suspend-backend b))))
(is (null (multiple-value-list (resume-backend b))))
(shutdown-backend b)))
(test sync-is-noop-on-simple