fe7bbf09 |
(load #p"~/quicklisp/setup.lisp")
(declaim (optimize (speed 0) (safety 3) (debug 3)))
(ql:quickload :cl-charms)
(ql:quickload :cells)
(ql:quickload :alexandria)
(ql:quickload :anaphora)
(defpackage :lisp-edit
(:use :cl :cl-charms :cells :alexandria :anaphora))
(in-package :lisp-edit)
(defun apply-cursor-action (action)
(case action
((:up) (cons 0 -1))
((:down) (cons 0 1))
((:left) (cons -1 0))
((:right) (cons 1 0))))
(defun dispatch-char (c)
(case c
((nil) nil)
((#\j) :down )
((#\h) :left )
((#\k) :up )
((#\l) :right)
((#\f) :paint)
((#\q #\Q) :quit)))
(defmodel input-handler ()
((input-key :cell :ephemeral :accessor input-key :initform (c-in nil))
(action :cell :ephemeral :accessor action :initform (c? (dispatch-char (^input-key))))))
(defmodel cursor ()
((action :cell :ephemeral :initarg :action :accessor action :initform (c-in nil))
|
8b70612f |
(current-char :initarg :current-char :accessor current-char :initform (c-in nil))
|
fe7bbf09 |
(delta :accessor cursor-delta :initform
(c? (apply-cursor-action (^action))))
(x :accessor cursor-x :initarg :x :initform (c... (0) (car (^cursor-delta))))
(y :accessor cursor-y :initarg :y :initform (c... (0) (cdr (^cursor-delta))))))
|
8b70612f |
(defun map-actions (current-char action)
(case action
((:paint) (case current-char
((#\#) #\Space)
((#\Space) #\*)
((#\*) #\#)))))
(defmodel painter ()
((action :cell :ephemeral :initarg :action :accessor action :initform (c-in nil))
(current-char :initarg :current-char :accessor current-char :initform (c-in nil))
(output :cell :ephemeral :accessor output :initform (c? (map-actions (^current-char)
(^action))))))
|
fe7bbf09 |
(defvar *input-handler*)
(defvar *painter*)
(defvar *cursor*)
(defun paint ()
(with-restored-cursor *standard-window*
(write-char-at-cursor *standard-window*
(if (char/= #\Space (char-at-cursor *standard-window*))
#\Space
#\*))))
(defun constrain-pos (elem pos delta window-dimensions)
(let* ((accessor (case elem
((:x) #'car)
((:y) #'cdr)))
(window-elem (funcall accessor window-dimensions))
(delta-elem (funcall accessor delta))
(pos-elem (funcall accessor pos))
(naive-delta (mod (+ pos-elem delta-elem)
window-elem))
(real-delta (- naive-delta pos-elem)))
real-delta))
(defun main ()
(cells-reset)
(handler-case
(with-curses ()
(disable-echoing)
(enable-raw-input :interpret-control-characters t)
(enable-non-blocking-mode *standard-window*)
|
8b70612f |
(setf *input-handler* (make-instance 'input-handler)
*cursor* (make-instance 'cursor :action (c? (action *input-handler*)))
*painter* (make-instance 'painter
:action (c? (action *input-handler*))
:current-char (c? (current-char *cursor*))))
(let* ((screen-dimensions (multiple-value-bind (x y) (window-dimensions *standard-window*)
(cons x y))))
(labels
((set-current-char (cursor)
(with-restored-cursor *standard-window*
(setf (current-char cursor) (char-at-cursor *standard-window*))))
(mvc (x y)
(move-cursor *standard-window* x y)
(set-current-char *cursor*))
(constrain-coordinate (side coord)
(mod coord (funcall
(ecase side
((:x) #'car)
((:y) #'cdr))
screen-dimensions))))
(defobserver output ((self painter))
(with-restored-cursor *standard-window*
(write-char-at-cursor *standard-window* new-value))
(with-integrity (:change)
(set-current-char *cursor*)))
(defobserver y ((self cursor))
(with-integrity (:change)
(let ((adjusted-n-v (constrain-coordinate :y new-value)))
(if (/= new-value adjusted-n-v)
(setf (^cursor-y) (- adjusted-n-v new-value))
(mvc (^cursor-x) adjusted-n-v)))))
(defobserver x ((self cursor))
(with-integrity (:change)
(let ((adjusted-n-v (constrain-coordinate :x new-value)))
(if (/= new-value adjusted-n-v)
(setf (^cursor-x) (- adjusted-n-v new-value))
(mvc adjusted-n-v (^cursor-y))))))
(mvc 0 0)
(loop :named driver-loop
:for c := (get-char *standard-window* :ignore-error t)
:do (progn
(refresh-window *standard-window*)
(setf (input-key *input-handler*) c))))))
|
fe7bbf09 |
(sb-sys:interactive-interrupt (c) (declare (ignore c)))))
|