diff --git a/demo b/demo new file mode 100755 index 0000000..0e2542e --- /dev/null +++ b/demo @@ -0,0 +1,20 @@ +#!/bin/sh +# cl-tty demo launcher +# Sets raw terminal mode, runs the demo, restores terminal on exit. +# This is needed because SBCL's --script mode + run-program combo +# can't reliably set raw mode from inside the Lisp process. + +SAVED=$(stty -g 2>/dev/null) +if [ -z "$SAVED" ]; then + echo "ERROR: Not running in a real terminal." >&2 + echo " Try: sbcl --script demo.lisp" >&2 + exit 1 +fi + +cleanup() { + stty "$SAVED" 2>/dev/null +} +trap cleanup EXIT INT TERM + +stty raw -echo -isig -icanon min 1 time 0 2>/dev/null +sbcl --script "$(dirname "$0")/demo.lisp" diff --git a/demo.lisp b/demo.lisp index d7eb880..fdb1d5c 100644 --- a/demo.lisp +++ b/demo.lisp @@ -124,60 +124,55 @@ t))) (defun run-demo () - (let ((saved (ignore-errors (set-raw-mode)))) - (unless saved - (format *error-output* "~&ERROR: Cannot set terminal to raw mode.~%") - (format *error-output* " Make sure you are in a real terminal (not a pipe/redirect).~%") - (format *error-output* " Try: sbcl --script demo.lisp~%") - (return-from run-demo)) + "Run the demo. Assumes raw terminal mode is already set by the +shell wrapper (./demo) or by running: + stty raw -echo -isig -icanon min 1 time 0 + sbcl --script demo.lisp" + (init-app-state) + (let* ((backend (detect-backend)) + (w 80) (h 24)) + (declare (ignore h)) + (initialize-backend backend) (unwind-protect - (progn - (init-app-state) - (let* ((backend (detect-backend)) - (w 80) (h 24)) - (initialize-backend backend) - (unwind-protect - (loop while (getf *app* :running) - do - (backend-clear backend) - ;; Title bar - (draw-border backend 2 1 (- w 4) 3 :style :double :title " cl-tty v0.15.0 ") - (draw-text backend 4 2 "arrows/tab: tabs type: test input mouse: test SGR q/esc: quit" - :bright-white nil) - ;; Tab bar - (loop for (label . idx) in '((" Home " . 0) (" Widgets " . 1) (" Console " . 2)) - for x-pos = 4 then (+ x-pos label-len 2) - for label-len = (length label) - do (let ((active (eql idx (getf *app* :tab)))) - (if active - (draw-text backend x-pos 4 label :bright-white :accent :bold t) - (draw-text backend x-pos 4 label :text-muted nil)))) - ;; Content area - (case (getf *app* :tab) - (0 (render-tab-home backend 4 6 72 20)) - (1 (render-tab-widgets backend 4 6 72 24 - (getf *app* :input) - (getf *app* :textarea))) - (2 (render-tab-console backend 4 6 72 16))) - ;; Mouse cursor indicator - (let ((mx (getf *app* :mouse-x)) - (my (getf *app* :mouse-y))) - (when (and (>= mx 0) (>= my 0)) - (draw-text backend mx my "@" :bright-cyan nil))) - ;; Status bar - (draw-rect backend 2 23 (- w 4) 1 :bg :blue) - (draw-text backend 4 23 - (format nil " Tab ~d/3 | ~d events " - (1+ (getf *app* :tab)) (length *log*)) - :bright-white :blue :bold t) - (finish-output *standard-output*) - ;; Read event — blocks until a key or mouse event arrives - (let ((event (read-event backend))) - (when event - (handle-event event)))) - (shutdown-backend backend)))) - (when saved - (restore-terminal-state saved))))) + (loop while (getf *app* :running) + do + (backend-clear backend) + ;; Title bar + (draw-border backend 2 1 (- w 4) 3 :style :double :title " cl-tty v0.15.0 ") + (draw-text backend 4 2 "arrows/tab: tabs type: test input mouse: test SGR q/esc: quit" + :bright-white nil) + ;; Tab bar + (loop for (label . idx) in '((" Home " . 0) (" Widgets " . 1) (" Console " . 2)) + for x-pos = 4 then (+ x-pos label-len 2) + for label-len = (length label) + do (let ((active (eql idx (getf *app* :tab)))) + (if active + (draw-text backend x-pos 4 label :bright-white :accent :bold t) + (draw-text backend x-pos 4 label :text-muted nil)))) + ;; Content area + (case (getf *app* :tab) + (0 (render-tab-home backend 4 6 72 20)) + (1 (render-tab-widgets backend 4 6 72 24 + (getf *app* :input) + (getf *app* :textarea))) + (2 (render-tab-console backend 4 6 72 16))) + ;; Mouse cursor indicator + (let ((mx (getf *app* :mouse-x)) + (my (getf *app* :mouse-y))) + (when (and (>= mx 0) (>= my 0)) + (draw-text backend mx my "@" :bright-cyan nil))) + ;; Status bar + (draw-rect backend 2 23 (- w 4) 1 :bg :blue) + (draw-text backend 4 23 + (format nil " Tab ~d/3 | ~d events " + (1+ (getf *app* :tab)) (length *log*)) + :bright-white :blue :bold t) + (finish-output *standard-output*) + ;; Read event — blocks until a key or mouse event arrives + (let ((event (read-event backend))) + (when event + (handle-event event)))) + (shutdown-backend backend)))) (run-demo) (uiop:quit 0)