raart/lux-chaos.rkt

172 lines
5.0 KiB
Racket

#lang racket/base
(require racket/match
racket/contract/base
racket/list
racket/set
racket/async-channel
racket/system
ansi
unix-signals
lux/chaos
raart/draw
raart/buffer
(submod raart/buffer internal)
struct-define)
(struct term (f in out))
(define-syntax-rule (define-stty-term open-term close-term)
(begin
(define default-tty "/dev/tty")
(define stty-minus-f-arg-string
(case (system-type 'os)
((macosx) "-f")
(else "-F")))
(define (open-term #:tty [tty default-tty])
(system* "/bin/stty" stty-minus-f-arg-string tty
"raw" "pass8" "-echo")
(define-values (in out)
(open-input-output-file tty #:exists 'update))
(file-stream-buffer-mode in 'none)
(file-stream-buffer-mode out 'none)
(term tty in out))
(define (close-term t)
(match-define (term tty in out) t)
(close-input-port in)
(close-output-port out)
(system* "/bin/stty" stty-minus-f-arg-string tty
"sane"))))
(define-syntax-rule (define-stdin-term open-term close-term)
(begin
(require ansi/private/tty-raw-extension)
(define (open-term #:tty [tty #f])
(when tty
(error 'open-term "Custom tty not supported in this version"))
(tty-raw!)
(term #f (current-input-port) (current-output-port)))
(define (close-term t)
(tty-restore!))))
#;(define-stty-term open-term close-term)
(define-stdin-term open-term close-term)
(define (display/term t v)
(define op (term-out t))
(unless (port-closed? op)
(display v op)
(flush-output op)))
;; Lux
(define x11-mouse-on
(string-append (set-mode x11-focus-event-mode)
(set-mode x11-any-event-mouse-tracking-mode)
(set-mode x11-extended-mouse-tracking-mode)))
(define x11-mouse-off
(string-append (reset-mode x11-extended-mouse-tracking-mode)
(reset-mode x11-any-event-mouse-tracking-mode)
(reset-mode x11-focus-event-mode)))
(define (convert-key v)
(match v
[(key value mods)
(format "~a~a~a~a"
(if (set-member? mods 'meta) "M-" "")
(if (set-member? mods 'control) "C-" "")
(if (set-member? mods 'shift) "S-" "")
(if (char? value)
value
(format "<~a>" value)))]
[_ v]))
(define (make-raart #:mouse? [mouse? #f])
(define alternate? #t)
(define ch (make-async-channel))
(*term alternate? mouse? #f #f ch #f #f #f #f))
(define-struct-define term-define *term)
(struct *term
(alternate? mouse? t buf ch sig-th input-th rows cols)
#:mutable
#:methods gen:chaos
[(define (chaos-event c)
(term-define c)
(handle-evt ch
(match-lambda
[(and e (screen-size-report new-rows new-cols))
(set! rows new-rows)
(set! cols new-cols)
(buffer-resize! buf rows cols)
e]
[e e])))
(define (chaos-output! c o)
(when o
(draw (*term-buf c) o)))
(define (chaos-label! c l)
(display/term (*term-t c) (xterm-set-window-title l)))
(define (chaos-start! c)
(term-define c)
(set! t (open-term))
(set! rows 24)
(set! cols 80)
(set! buf (make-cached-buffer rows cols #:output (term-out t)))
;; Initialize term
(when alternate?
(display/term t (set-mode alternate-screen-mode)))
(when mouse?
(display/term t x11-mouse-on)
(plumber-add-flush! (current-plumber)
(lambda (handle)
(display/term t x11-mouse-off))))
;; Listen for input
(set! input-th
(thread
(λ ()
(let loop ()
(define v (lex-lcd-input (term-in t) #:utf-8? #t))
(unless (eof-object? v)
(when (or (any-mouse-event? v)
(screen-size-report? v)
(key? v))
(async-channel-put ch (convert-key v)))
(loop))))))
;; Register for window change events
(display/term t (device-request-screen-size))
(set! sig-th
(thread
(λ ()
(let loop ()
(define s (read-signal))
(match (lookup-signal-name s)
['SIGWINCH (display/term t (device-request-screen-size))
(loop)])))))
(capture-signal! 'SIGWINCH)
(void))
(define (chaos-stop! c)
(term-define c)
(release-signal! 'SIGWINCH)
(kill-thread sig-th)
(kill-thread input-th)
(when mouse?
(display/term t x11-mouse-off))
(when alternate?
(display/term t (reset-mode alternate-screen-mode)))
(close-term t))])
(provide
(struct-out screen-size-report)
(struct-out any-mouse-event)
(struct-out mouse-focus-event)
(struct-out mouse-event)
(contract-out
[make-raart
(->* () (#:mouse? boolean?) chaos?)]))