src/high-level/bearlibterminal.lisp @ a70cb449bc42
Add LICENSE
author |
Steve Losh <steve@stevelosh.com> |
date |
Tue, 14 Mar 2017 13:34:26 +0000 |
parents |
05bcf3c72a27 |
children |
e95ecd878abd |
(in-package :bearlibterminal/high-level)
; (sb-int:set-floating-point-modes :traps nil)
;;;; Utils --------------------------------------------------------------------
(declaim (inline color-float-to-byte rgba rgbaf))
(defun pr (val)
(format t "~S~%" val)
(finish-output)
(values))
(defmacro -<> (expr &rest forms)
"Thread the given forms, with `<>` as a placeholder."
;; I am going to lose my fucking mind if I have to program lisp without
;; a threading macro, but I don't want to add another dep to this library, so
;; here we are.
`(let* ((<> ,expr)
,@(mapcar (lambda (form)
(if (symbolp form)
`(<> (,form <>))
`(<> ,form)))
forms))
<>))
(deftype color-float ()
'(single-float 0.0 1.0))
(deftype color-byte ()
'(unsigned-byte 8))
(deftype color ()
'(unsigned-byte 32))
(defun color-float-to-byte (float)
(truncate (* float 255.0)))
(defun rgba (r g b a)
(-<> 0
(dpb a (byte 8 24) <>)
(dpb r (byte 8 16) <>)
(dpb g (byte 8 8) <>)
(dpb b (byte 8 0) <>)))
(defun rgbaf (r g b a)
(declare (optimize speed)
(type color-float r g b a))
(rgba (color-float-to-byte r)
(color-float-to-byte g)
(color-float-to-byte b)
(color-float-to-byte a)))
(defun color-name (color-name)
(blt/ll:color-from-name color-name))
(defun boolean-to-onoff (boolean)
(if boolean
blt/ll:+tk-on+
blt/ll:+tk-off+))
(defun onoff-to-boolean (onoff)
(ecase onoff
(blt/ll:+tk-on+ t)
(blt/ll:+tk-off+ nil)))
(defun int-to-boolean (int)
(not (zerop int)))
(defun state-boolean (state)
(int-to-boolean (blt/ll:terminal-state state)))
(defun character-to-code-point (character)
;; These seem to work in SBCL, ABCL, CCL, and ECL, but I need to do more
;; digging before I'm convinced.
(char-code character))
(defun code-point-to-character (code-point)
;; These seem to work in SBCL, ABCL, CCL, and ECL, but I need to do more
;; digging before I'm convinced.
(code-char code-point))
(defun horizontal-alignment (alignment-keyword)
(ccase alignment-keyword
(:default blt/ll:+tk-align-default+)
(:left blt/ll:+tk-align-left+)
(:right blt/ll:+tk-align-right+)
((:middle :center) blt/ll:+tk-align-center+)))
(defun vertical-alignment (alignment-keyword)
(ccase alignment-keyword
(:default blt/ll:+tk-align-default+)
(:top blt/ll:+tk-align-top+)
(:bottom blt/ll:+tk-align-bottom+)
((:middle :center) blt/ll:+tk-align-middle+)))
;;;; Error Checking -----------------------------------------------------------
(define-condition bearlibterminal-error (error) ())
(defun check (return-value)
(if (zerop return-value)
(error 'bearlibterminal-error)))
;;;; Wrappers -----------------------------------------------------------------
; Initialization and configuration: set
; Output: put ext, measure
; Input: read str
(defun open ()
(check (blt/ll:terminal-open)))
(defun close ()
(blt/ll:terminal-close))
(defun set (configuration-string)
(check (blt/ll:terminal-set-8 configuration-string)))
(defun refresh ()
(blt/ll:terminal-refresh))
(defun clear ()
(blt/ll:terminal-clear))
(defun clear-area (x y width height)
(blt/ll:terminal-clear-area x y width height))
(defun crop (x y width height)
(blt/ll:terminal-crop x y width height))
(defun layer ()
(blt/ll:terminal-state blt/ll:+tk-layer+))
(defun (setf layer) (new-value)
(blt/ll:terminal-layer new-value)
new-value)
(defun color ()
(blt/ll:terminal-state blt/ll:+tk-color+))
(defun (setf color) (color)
(blt/ll:terminal-color color))
(defun background-color ()
(blt/ll:terminal-state blt/ll:+tk-bkcolor+))
(defun (setf background-color) (color)
(blt/ll:terminal-bkcolor color))
(defun composition ()
(onoff-to-boolean (blt/ll:terminal-state blt/ll:+tk-composition+)))
(defun (setf composition) (new-value)
(blt/ll:terminal-composition (boolean-to-onoff new-value))
new-value)
(defun has-input-p ()
(int-to-boolean (blt/ll:terminal-has-input)))
(defun read ()
(blt/ll:terminal-read))
(defun peek ()
(blt/ll:terminal-peek))
(defun sleep (seconds)
(blt/ll:terminal-delay (truncate (* seconds 1000))))
(defun width ()
(blt/ll:terminal-state blt/ll:+tk-width+))
(defun height ()
(blt/ll:terminal-state blt/ll:+tk-height+))
(defun cell-code (x y &optional (index 0))
(let ((code (blt/ll:terminal-pick x y index)))
(if (zerop code)
nil
code)))
(defun cell-char (x y &optional (index 0))
(let ((code (cell-code x y index)))
(when code (code-point-to-character code))))
(defun (setf cell-code) (code-point x y)
(blt/ll:terminal-put x y code-point))
(defun (setf cell-char) (character x y)
(blt/ll:terminal-put x y (character-to-code-point character)))
(defun cell-color (x y &optional (index 0))
(blt/ll:terminal-pick-color x y index))
(defun cell-background-color (x y)
(blt/ll:terminal-pick-bkcolor x y))
(defun print (x y string &key
width
height
(halign :default)
(valign :default))
(cffi:with-foreign-objects ((measured-width :int)
(measured-height :int))
(blt/ll:terminal-print-ext-8 x y
(or width 0)
(or height 0)
(logior (horizontal-alignment halign)
(vertical-alignment valign))
string
measured-width
measured-height)
(values (cffi:mem-ref measured-width :int)
(cffi:mem-ref measured-height :int))))
;;;; Higher-Level API ---------------------------------------------------------
(defmacro defuck-floats (&body body)
#+sbcl
`(sb-int:with-float-traps-masked
(:inexact :underflow :overflow :invalid :divide-by-zero)
,@body)
#-(or sbcl)
`(progn ,@body))
(defmacro with-terminal (&body body)
`(defuck-floats
(open)
(unwind-protect
(progn ,@body)
(close))))
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun find-integer (event)
(ecase event
(:A blt/ll:+tk-a+)
(:B blt/ll:+tk-b+)
(:C blt/ll:+tk-c+)
(:D blt/ll:+tk-d+)
(:E blt/ll:+tk-e+)
(:F blt/ll:+tk-f+)
(:G blt/ll:+tk-g+)
(:H blt/ll:+tk-h+)
(:I blt/ll:+tk-i+)
(:J blt/ll:+tk-j+)
(:K blt/ll:+tk-k+)
(:L blt/ll:+tk-l+)
(:m blt/ll:+tk-m+)
(:n blt/ll:+tk-n+)
(:o blt/ll:+tk-o+)
(:p blt/ll:+tk-p+)
(:q blt/ll:+tk-q+)
(:r blt/ll:+tk-r+)
(:s blt/ll:+tk-s+)
(:t blt/ll:+tk-t+)
(:u blt/ll:+tk-u+)
(:v blt/ll:+tk-v+)
(:w blt/ll:+tk-w+)
(:x blt/ll:+tk-x+)
(:y blt/ll:+tk-y+)
(:z blt/ll:+tk-z+)
(:1 blt/ll:+tk-1+)
(:2 blt/ll:+tk-2+)
(:3 blt/ll:+tk-3+)
(:4 blt/ll:+tk-4+)
(:5 blt/ll:+tk-5+)
(:6 blt/ll:+tk-6+)
(:7 blt/ll:+tk-7+)
(:8 blt/ll:+tk-8+)
(:9 blt/ll:+tk-9+)
(:0 blt/ll:+tk-0+)
(:return blt/ll:+tk-return+)
(:enter blt/ll:+tk-enter+)
(:escape blt/ll:+tk-escape+)
(:backspace blt/ll:+tk-backspace+)
(:tab blt/ll:+tk-tab+)
(:space blt/ll:+tk-space+)
(:minus blt/ll:+tk-minus+)
(:equals blt/ll:+tk-equals+)
(:lbracket blt/ll:+tk-lbracket+)
(:rbracket blt/ll:+tk-rbracket+)
(:backslash blt/ll:+tk-backslash+)
(:semicolon blt/ll:+tk-semicolon+)
(:apostrophe blt/ll:+tk-apostrophe+)
(:grave blt/ll:+tk-grave+)
(:comma blt/ll:+tk-comma+)
(:period blt/ll:+tk-period+)
(:slash blt/ll:+tk-slash+)
(:f1 blt/ll:+tk-f-1+)
(:f2 blt/ll:+tk-f-2+)
(:f3 blt/ll:+tk-f-3+)
(:f4 blt/ll:+tk-f-4+)
(:f5 blt/ll:+tk-f-5+)
(:f6 blt/ll:+tk-f-6+)
(:f7 blt/ll:+tk-f-7+)
(:f8 blt/ll:+tk-f-8+)
(:f9 blt/ll:+tk-f-9+)
(:f10 blt/ll:+tk-f-10+)
(:f11 blt/ll:+tk-f-11+)
(:f12 blt/ll:+tk-f-12+)
(:pause blt/ll:+tk-pause+)
(:insert blt/ll:+tk-insert+)
(:home blt/ll:+tk-home+)
(:page-up blt/ll:+tk-pageup+)
(:delete blt/ll:+tk-delete+)
(:end blt/ll:+tk-end+)
(:page-down blt/ll:+tk-pagedown+)
(:right blt/ll:+tk-right+)
(:left blt/ll:+tk-left+)
(:down blt/ll:+tk-down+)
(:up blt/ll:+tk-up+)
(:numpad-divide blt/ll:+tk-kp-divide+)
(:numpad-multiply blt/ll:+tk-kp-multiply+)
(:numpad-minus blt/ll:+tk-kp-minus+)
(:numpad-plus blt/ll:+tk-kp-plus+)
(:numpad-enter blt/ll:+tk-kp-enter+)
(:numpad-1 blt/ll:+tk-kp-1+)
(:numpad-2 blt/ll:+tk-kp-2+)
(:numpad-3 blt/ll:+tk-kp-3+)
(:numpad-4 blt/ll:+tk-kp-4+)
(:numpad-5 blt/ll:+tk-kp-5+)
(:numpad-6 blt/ll:+tk-kp-6+)
(:numpad-7 blt/ll:+tk-kp-7+)
(:numpad-8 blt/ll:+tk-kp-8+)
(:numpad-9 blt/ll:+tk-kp-9+)
(:numpad-0 blt/ll:+tk-kp-0+)
(:numpad-period blt/ll:+tk-kp-period+)
(:shift blt/ll:+tk-shift+)
(:control blt/ll:+tk-control+)
(:alt blt/ll:+tk-alt+)
(:mouse-left blt/ll:+tk-mouse-left+)
(:mouse-right blt/ll:+tk-mouse-right+)
(:mouse-middle blt/ll:+tk-mouse-middle+)
(:mouse-x1 blt/ll:+tk-mouse-x-1+)
(:mouse-x2 blt/ll:+tk-mouse-x-2+)
(:mouse-move blt/ll:+tk-mouse-move+)
(:mouse-scroll blt/ll:+tk-mouse-scroll+)
(:close blt/ll:+tk-close+)
(:resize blt/ll:+tk-resized+)
(:none blt/ll:+tk-input-none+)
(:cancelled blt/ll:+tk-input-cancelled+))))
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun parse-key-case-head (head data-symbol)
(if (eq t head)
t
(destructuring-bind (event &rest modifiers)
(ensure-list head)
(let* ((up (member :up modifiers))
(down (member :down modifiers))
(up/down (cond ((and up down) :both)
(up :up)
(down :down)
(t :down)))
(shift (ensure-boolean
(member :shift modifiers)))
(alt (ensure-boolean
(intersection modifiers
'(:alt :option :meta))))
(control (ensure-boolean
(intersection modifiers
'(:control :command)))))
`(and
,(ecase up/down
(:both `(eql (logand ,data-symbol
,(lognot blt/ll:+tk-key-released+))
,(find-integer event)))
(:up `(eql ,data-symbol
,(logior (find-integer event)
blt/ll:+tk-key-released+)))
(:down `(eql ,data-symbol
,(find-integer event))))
(,(if shift 'progn 'not)
(state-boolean blt/ll:+tk-shift+))
(,(if control 'progn 'not)
(state-boolean blt/ll:+tk-control+))
(,(if alt 'progn 'not)
(state-boolean blt/ll:+tk-alt+))))))))
(defmacro key-case (data &rest clauses)
(once-only (data)
`(cond ,@(loop :for (head . body) :in clauses
:collect `(,(parse-key-case-head head data) ,@body)))))
;;;; Scratch ------------------------------------------------------------------
(defun test ()
(trivial-main-thread:with-body-in-main-thread (:blocking t)
(with-terminal
(refresh)
(set "input.filter = [keyboard+, mouse+]")
(loop
:for data = (read)
:do (pr data)
:while (key-case data
((:a :down) (pr "A down") t)
((:a :up) (pr "A up") t)
((:a :control) (pr "ctrl a") t)
((:b :control :shift) (pr "shift-ctrl b") t)
((:b :down :up) (pr "B down or up") t)
(:escape nil)
(t (pr "something else") t))))))