raart/buffer.rkt

288 lines
9.7 KiB
Racket
Raw Normal View History

2018-01-03 03:05:06 +00:00
#lang racket/base
(require racket/generic
racket/match
racket/contract/base
2018-01-03 23:01:23 +00:00
(prefix-in A: ansi)
2018-01-04 21:48:07 +00:00
struct-define)
2018-01-03 21:32:16 +00:00
2018-01-03 03:05:06 +00:00
(define-generics buffer
(buffer-resize! buffer rows cols)
(buffer-start! buffer rows cols)
2018-08-17 17:15:07 +00:00
(buffer-commit! buffer #:cursor? [cursor?]))
2018-01-03 03:05:06 +00:00
(define symbol->style
`#hasheq([normal . ,A:style-normal]
[bold . ,A:style-bold]
[inverse . ,A:style-inverse]
[underline . ,A:style-underline]))
(define style/c (apply or/c (hash-keys symbol->style)))
(define symbol->color
`#hasheq(
[black . 0] [red . 1] [green . 2] [yellow . 3]
[blue . 4] [magenta . 5] [cyan . 6] [white . 7]
[brblack . 8] [brred . 9] [brgreen . 10] [bryellow . 11]
[brblue . 12] [brmagenta . 13] [brcyan . 14] [brwhite . 15]))
2020-04-16 14:23:27 +00:00
(define color/c (apply or/c byte? #f (hash-keys symbol->color)))
(define (color->code c)
(if (byte? c) c
(hash-ref symbol->color c)))
2018-01-03 03:05:06 +00:00
(define (select-style* s)
2019-07-13 00:37:46 +00:00
(define (k s) (A:select-graphic-rendition (hash-ref symbol->style s)))
(if (eq? s 'normal) (k s)
(string-append (k 'normal) (k s))))
2018-01-03 03:05:06 +00:00
(define (select-text-color* c)
(if c
2020-04-16 14:23:27 +00:00
(A:select-xterm-256-text-color (color->code c))
2018-01-03 03:05:06 +00:00
(A:select-graphic-rendition A:style-default-text-color)))
(define (select-background-color* c)
(if c
2020-04-16 14:23:27 +00:00
(A:select-xterm-256-background-color (color->code c))
2018-01-03 03:05:06 +00:00
(A:select-graphic-rendition A:style-default-background-color)))
(define (make-terminal-buffer term-rows term-cols
#:clear? [clear? #t]
#:output [op (current-output-port)])
(terminal-buffer clear? op term-rows term-cols))
2018-01-08 18:45:24 +00:00
(define-struct-define terminal-buffer-define terminal-buffer)
2018-01-03 03:33:30 +00:00
(struct terminal-buffer (clear? op [term-rows #:mutable] [term-cols #:mutable])
2018-01-03 03:05:06 +00:00
#:methods gen:buffer
[(define (buffer-resize! buf new-rows new-cols)
2018-01-03 23:18:40 +00:00
(terminal-buffer-define buf)
(set! term-rows new-rows)
(set! term-cols new-cols))
2018-01-03 03:05:06 +00:00
(define (buffer-start! buf draw-rows draw-cols)
2018-01-03 23:18:40 +00:00
(terminal-buffer-define buf)
2018-01-03 03:33:30 +00:00
(define-syntax-rule
(maybe-update last-X X select-X)
(unless (eq? last-X X)
(display (select-X X) op)
(set! last-X X)))
2018-01-03 03:05:06 +00:00
(display (A:dec-soft-terminal-reset) op)
(when (terminal-buffer-clear? buf)
(display (A:clear-screen/home) op))
(display (A:hide-cursor) op)
2019-07-13 00:37:46 +00:00
(define last-s #f)
2018-01-03 03:33:30 +00:00
(define last-f #f)
(define last-b #f)
2018-12-12 01:16:27 +00:00
(define cur-r -1)
(define cur-c -1)
2018-01-03 20:50:00 +00:00
(values
2018-01-03 23:18:40 +00:00
term-rows term-cols
2018-01-03 20:50:00 +00:00
(λ (s f b r c ch)
(cond
[(or (< r 0)
2018-01-03 23:18:40 +00:00
(<= term-rows r)
2018-01-03 20:50:00 +00:00
(< c 0)
2018-01-03 23:18:40 +00:00
(<= term-cols c))
2018-01-03 20:50:00 +00:00
#f]
[else
(maybe-update last-s s select-style*)
(maybe-update last-f f select-text-color*)
(maybe-update last-b b select-background-color*)
2018-01-03 03:33:30 +00:00
2018-01-03 20:50:00 +00:00
(define tr (add1 r))
(define tc (add1 c))
(unless (and (= cur-r tr)
(= cur-c tc))
(display (A:goto tr tc) op)
(set! cur-r tr)
(set! cur-c tc))
2018-01-03 03:39:24 +00:00
2018-01-03 20:50:00 +00:00
(when ch
(display ch op)
(set! cur-c (add1 cur-c)))
2018-01-03 03:39:24 +00:00
2018-01-03 20:50:00 +00:00
#t]))))
2018-08-17 17:15:07 +00:00
(define (buffer-commit! buf #:cursor? [cursor? #t])
2018-01-03 23:18:40 +00:00
(terminal-buffer-define buf)
2018-08-17 17:15:07 +00:00
(when cursor? (display (A:show-cursor) op))
2018-01-03 03:05:06 +00:00
(flush-output op))])
2018-01-03 21:32:16 +00:00
(struct output-cell (s f b ch) #:mutable #:transparent)
2018-01-03 03:05:06 +00:00
(define (clear-cell! c)
(set-output-cell-s! c 'normal)
(set-output-cell-f! c #f)
(set-output-cell-b! c #f)
(set-output-cell-ch! c #f))
(define (default-cell) (output-cell 'normal #f #f #f))
2018-01-03 03:33:30 +00:00
(struct cells (rows cols vec) #:mutable)
(define (maybe-make-cells old new-rows new-cols)
(match-define (cells old-rows old-cols vec) old)
2018-01-10 18:46:02 +00:00
;; XXX support shrinking/growing while preserving information
2018-01-03 03:33:30 +00:00
(if (and (= old-rows new-rows)
(= old-cols new-cols))
old
(make-cells new-rows new-cols)))
2018-01-03 03:05:06 +00:00
(define (make-cells rows cols)
2018-01-03 03:33:30 +00:00
(cells rows cols
(build-vector
rows
(λ (r)
(build-vector cols (λ (c) (default-cell)))))))
(define (clear-cells! cs)
(match-define (cells _ _ vec) cs)
(for* ([row (in-vector vec)]
[cell (in-vector row)])
(clear-cell! cell)))
(define (draw-cell! cs)
2018-01-03 03:39:24 +00:00
(match-define (cells ok-rows ok-cols vec) cs)
2018-01-03 03:33:30 +00:00
(λ (s f b r c ch)
2018-01-03 03:39:24 +00:00
(cond
[(or (< r 0)
(<= ok-rows r)
(< c 0)
(<= ok-cols c))
#f]
[else
(define oc (vector-ref (vector-ref vec r) c))
(set-output-cell-s! oc s)
(set-output-cell-f! oc f)
(set-output-cell-b! oc b)
2018-01-03 21:32:16 +00:00
(when ch
(set-output-cell-ch! oc ch))
2018-01-03 03:39:24 +00:00
#t])))
2018-01-03 03:05:06 +00:00
(define (make-output-buffer #:output [op (current-output-port)])
2018-01-03 03:33:30 +00:00
(output-buffer op (make-cells 0 0)))
2018-01-08 18:45:24 +00:00
(define-struct-define output-buffer-define output-buffer)
2018-01-03 03:33:30 +00:00
(struct output-buffer (op [cells #:mutable])
2018-01-03 03:05:06 +00:00
#:methods gen:buffer
[(define (buffer-resize! buf new-rows new-cols)
2018-01-03 23:18:40 +00:00
(output-buffer-define buf)
2018-01-03 23:01:23 +00:00
(set! cells (maybe-make-cells cells new-rows new-cols)))
2018-01-03 03:05:06 +00:00
(define (buffer-start! buf draw-rows draw-cols)
2018-01-03 23:18:40 +00:00
(output-buffer-define buf)
2018-01-03 03:05:06 +00:00
(buffer-resize! buf draw-rows draw-cols)
2018-01-03 23:02:55 +00:00
(clear-cells! cells)
(values draw-rows draw-cols (draw-cell! cells)))
2018-08-17 17:15:07 +00:00
(define (buffer-commit! buf #:cursor? [cursor? #t])
2018-01-03 23:18:40 +00:00
(output-buffer-define buf)
2019-07-13 00:37:46 +00:00
(for/fold ([last-s #f] [last-f #f] [last-b #f])
2018-01-03 23:02:55 +00:00
([row (in-vector (cells-vec cells))])
2018-01-03 03:05:06 +00:00
(begin0
(for/fold ([last-s last-s] [last-f last-f] [last-b last-b])
([oc (in-vector row)])
(match-define (output-cell s f b ch) oc)
(unless (eq? last-s s)
(display (select-style* s) op))
(unless (eq? last-f f)
(display (select-text-color* f) op))
(unless (eq? last-b b)
(display (select-background-color* b) op))
(display (or ch #\space) op)
(values s f b))
(newline op)))
(flush-output op)
(void))])
2018-01-03 21:32:16 +00:00
(define (make-cached-buffer term-rows term-cols
#:output [op (current-output-port)])
(define (mk-term clear?)
(make-terminal-buffer term-rows term-cols
#:clear? clear?
#:output op))
(cached-buffer
#t
(mk-term #f) (mk-term #t)
term-rows term-cols
(make-cells term-rows term-cols)
(make-cells term-rows term-cols)
0 0))
2018-01-08 18:45:24 +00:00
(define-struct-define cached-buffer-define cached-buffer)
2018-01-03 21:32:16 +00:00
(struct cached-buffer
([clear-next? #:mutable]
term-nclear term-yclear
[term-rows #:mutable] [term-cols #:mutable]
[cur-cells #:mutable] [new-cells #:mutable]
[last-row #:mutable] [last-col #:mutable])
#:methods gen:buffer
[(define/generic super-buffer-resize! buffer-resize!)
(define/generic super-buffer-start! buffer-start!)
(define/generic super-buffer-commit! buffer-commit!)
(define (buffer-resize! buf new-rows new-cols)
2018-01-03 23:18:40 +00:00
(cached-buffer-define buf)
(set! clear-next? #t)
(set! cur-cells (maybe-make-cells cur-cells new-rows new-cols))
(set! new-cells (maybe-make-cells new-cells new-rows new-cols))
2018-01-03 23:18:40 +00:00
(super-buffer-resize! term-nclear new-rows new-cols)
(super-buffer-resize! term-yclear new-rows new-cols)
(set! term-rows new-rows)
(set! term-cols new-cols)
(clear-cells! cur-cells))
2018-01-03 21:32:16 +00:00
(define (buffer-start! buf draw-rows draw-cols)
2018-01-03 23:18:40 +00:00
(cached-buffer-define buf)
(clear-cells! new-cells)
(define dc (draw-cell! new-cells))
(values term-rows term-cols
2018-01-03 21:32:16 +00:00
(λ (s f b r c ch)
2018-01-03 23:18:40 +00:00
(set! last-row r)
(set! last-col c)
2018-01-03 21:32:16 +00:00
(dc s f b r c ch))))
2018-08-17 17:15:07 +00:00
(define (buffer-commit! buf #:cursor? [cursor? #t])
2018-01-03 23:18:40 +00:00
(cached-buffer-define buf)
(define inner-buf (if clear-next? term-yclear term-nclear))
(set! clear-next? #f)
(define-values (ok-rows ok-cols draw!)
(super-buffer-start! inner-buf term-rows term-cols))
(for ([cur-row (in-vector (cells-vec cur-cells))]
[new-row (in-vector (cells-vec new-cells))]
2018-01-03 21:32:16 +00:00
[r (in-naturals)])
(for ([cur-cell (in-vector cur-row)]
[new-cell (in-vector new-row)]
[c (in-naturals)])
(unless (equal? cur-cell new-cell)
(match-define (output-cell _ _ _ cur-ch) cur-cell)
(match-define (output-cell s f b new-ch) new-cell)
(draw! s f b r c (or new-ch #\space)))))
2018-01-03 23:18:40 +00:00
(draw! 'normal #f #f last-row last-col #f)
2018-08-17 17:15:07 +00:00
(super-buffer-commit! inner-buf #:cursor? cursor?)
2018-01-03 23:18:40 +00:00
(swap! new-cells cur-cells))])
(define-syntax-rule (swap! x y)
(let ([tmp x])
(set! x y)
(set! y tmp)))
2018-01-03 03:05:06 +00:00
2018-08-21 20:28:29 +00:00
(module+ internal
(provide
(contract-out
[buffer-resize!
(-> buffer?
exact-nonnegative-integer? exact-nonnegative-integer?
void?)]
[buffer-start!
(-> buffer?
exact-nonnegative-integer? exact-nonnegative-integer?
(values exact-nonnegative-integer?
exact-nonnegative-integer?
(-> style/c color/c color/c
exact-nonnegative-integer?
exact-nonnegative-integer?
(or/c char? #f)
boolean?)))]
[buffer-commit!
(->* (buffer?) (#:cursor? boolean?) void?)])))
2018-01-16 00:48:30 +00:00
2018-01-03 03:05:06 +00:00
(provide
(contract-out
[color/c contract?]
[style/c contract?]
[buffer? (-> any/c boolean?)]
[make-terminal-buffer
(->* (exact-nonnegative-integer? exact-nonnegative-integer?)
(#:clear? boolean? #:output output-port?)
buffer?)]
[make-output-buffer
(->* () (#:output output-port?) buffer?)]
2018-01-03 21:32:16 +00:00
[make-cached-buffer
2018-01-03 03:05:06 +00:00
(->* (exact-nonnegative-integer? exact-nonnegative-integer?)
(#:output output-port?)
buffer?)]))
2018-01-16 00:48:30 +00:00