commit 24f97a5702c9ff37a23bd5170bb5a69d1507e170
parent 7654bf9d7a108891ccce09d14fb0bcb339fd20e3
Author: Tomas Hlavaty <tom@logand.com>
Date: Sat, 1 Nov 2014 12:59:32 +0100
demo-zappel added
Diffstat:
2 files changed, 235 insertions(+), 0 deletions(-)
diff --git a/cl-rw-demo-zappel.asd b/cl-rw-demo-zappel.asd
@@ -0,0 +1,31 @@
+;;; -*- lisp; -*-
+
+;;; Copyright (C) 2013, 2014 Tomas Hlavaty <tom@logand.com>
+;;;
+;;; Permission is hereby granted, free of charge, to any person
+;;; obtaining a copy of this software and associated documentation
+;;; files (the "Software"), to deal in the Software without
+;;; restriction, including without limitation the rights to use, copy,
+;;; modify, merge, publish, distribute, sublicense, and/or sell copies
+;;; of the Software, and to permit persons to whom the Software is
+;;; furnished to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be
+;;; included in all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+;;; DEALINGS IN THE SOFTWARE.
+
+(asdf:defsystem :cl-rw-demo-zappel
+ :author "Tomas Hlavaty"
+ :maintainer "Tomas Hlavaty"
+ :licence "MIT"
+ :depends-on (:cl-rw)
+ :serial t
+ :components ((:file "demo-zappel")))
diff --git a/demo-zappel.lisp b/demo-zappel.lisp
@@ -0,0 +1,204 @@
+;;; Copyright (C) 2013, 2014 Tomas Hlavaty <tom@logand.com>
+;;;
+;;; Permission is hereby granted, free of charge, to any person
+;;; obtaining a copy of this software and associated documentation
+;;; files (the "Software"), to deal in the Software without
+;;; restriction, including without limitation the rights to use, copy,
+;;; modify, merge, publish, distribute, sublicense, and/or sell copies
+;;; of the Software, and to permit persons to whom the Software is
+;;; furnished to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be
+;;; included in all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+;;; DEALINGS IN THE SOFTWARE.
+
+(defpackage :rw.demo.zappel
+ (:use :cl))
+
+(in-package :rw.demo.zappel)
+
+(defun %random ()
+ (- #+linux
+ (with-open-file (s "/dev/urandom" :element-type '(unsigned-byte 8))
+ (read-byte s))
+ #-linux ;; TODO this returns always the same number, due to random state and threads
+ (random n)
+ 128))
+
+(defun draw-canvas (pos i seq click)
+ (flet ((hline (y x1 x2 stroke)
+ `((:line :x1 ,x1 :y1 ,y :x2 ,x2 :y2 ,y :stroke ,stroke))))
+ (let* ((n (length seq))
+ (w (* 10 (1- n)))
+ (h 300))
+ `((:svg :xmlns "http://www.w3.org/2000/svg"
+ :xmlns\:xlink "http://www.w3.org/1999/xlink"
+ :style "border:1px solid black"
+ :with ,w :height ,h :viewbox ,(format nil "0 0 ~d ~d" w h))
+ ((:text :x 20 :y 20) ,(aref seq (mod i n)))
+ ((:g
+ :transform ,(format nil "translate(0 ~d) scale(1 -1) translate(0.5 ~d.5)"
+ h (floor h 2)))
+ ,(hline 0 0 w "red")
+ ((:g :transform ,(format nil "translate(0 ~d)" pos))
+ ,(hline 0 0 w "blue")
+ ((:polyline
+ :fill "none" :stroke "green"
+ :points , (with-output-to-string (*standard-output*)
+ (loop
+ for j from 0 below n
+ for ii from (1+ i)
+ for x from 0 by 10
+ do (let ((y (aref seq (mod ii n))))
+ (when y
+ (write-char #\space)
+ (princ x)
+ (write-char #\,)
+ (princ y)))))))
+ ,@(loop
+ for j from 0 below n
+ for ii from (1+ i)
+ for x from 0 by 10
+ collect (let ((y (aref seq (mod ii n))))
+ (when y
+ `((:a :xlink\:href ,(funcall rw.ui::*click-link*
+ (lambda ()
+ (funcall click y))))
+ ((:circle :cx ,x :cy ,y :r 5 :fill "none"
+ :style "pointer-events:all"
+ :title ,(format nil "~d ~d" x y)))))))))))))
+
+(defun toplevel-widget ()
+ (let* (single
+ (delay 2)
+ (pos 0)
+ (i 0)
+ (n 61)
+ (seq (make-array n :initial-element nil))
+ clicked)
+ (lambda ()
+ (flet ((next ()
+ (setq i (mod (1+ i) n))
+ (setf (aref seq i) (%random))))
+ `(:html
+ (:head
+ ((:meta :http-equiv "Content-Type" :content "text/html;charset=utf-8"))
+ ((:meta :http-equiv "Pragma" :content "no-cache, no-store"))
+ ((:meta :http-equiv "Expires" :content -1))
+ ,@ (unless single
+ `(((:meta
+ :http-equiv "refresh"
+ :content ,(format nil "~d;url=~a" delay
+ (funcall rw.ui::*click-link* #'next))))))
+ (:title "Zappel demo")
+ (:<style
+ ;;(:td :padding "0.3em")
+ ;;(:th :padding "0.3em")
+ ))
+ (:body
+ ,(rw.ui:form
+ (flet ((menu (label click selected)
+ (if selected
+ `(:b ,label)
+ (rw.ui:link label click)))
+ (link (label click &optional (enabled t))
+ (rw.ui:link label click :enabled enabled)))
+ `(:div
+ (:h1 "Zappel demo")
+ (:p ,(menu "zappel" (lambda () (setq single nil)) (not single))
+ " | "
+ ,(menu "single" (lambda () (setq single t)) single))
+ (:hr)
+ (:p ,(link "faster"
+ (lambda () (setq delay (/ delay 2)))
+ (and (not single) (< 1 delay)))
+ " "
+ ,(link "slower"
+ (lambda () (setq delay (* 2 delay)))
+ (not single))
+ " " ,delay "s :: "
+ ,(link "step" #'next single))
+ (:p "clicked: " ,clicked)
+ (:p ,(draw-canvas pos i seq (lambda (x) (setq clicked x))))
+ (:p ,(link "center"
+ (lambda () (setq pos 0))
+ (not (zerop pos)))
+ " "
+ ,(link "up" (lambda () (incf pos 10)))
+ " "
+ ,(link "down" (lambda () (decf pos 10)))
+ " " ,pos "px"))))))))))
+
+(defun construct (sid aid renv)
+ (let ((prefix "/"))
+ (with-output-to-string (s)
+ (format s "~a?s=~a&a=~a" prefix sid aid)
+ (loop
+ for (k v) on renv by #'cddr
+ when v
+ do (format s "&~(~a~)=~a" k v)))))
+
+(defvar *query-parameters*)
+
+(defun query-parameter (key)
+ (cdr (assoc key *query-parameters* :test #'equal)))
+
+(defun deconstruct ()
+ (values (query-parameter "s")
+ (query-parameter "a")))
+
+(defun draw-zappel ()
+ (rw.ui:draw (lambda ()
+ (let ((w (toplevel-widget)))
+ (lambda ()
+ `(:http-1.0
+ :code 200
+ :headers (("Content-Type" . "text/html;charset=utf-8")
+ ("cache-control" . "no-cache,no-store")
+ ("pragma" . "no-cache")
+ ("expires" . "-1"))
+ :body ,(funcall w)))))
+ 'construct
+ 'deconstruct))
+
+(defun zappel-handler (msg stream method query protocol headers &optional body)
+ (declare (ignore protocol headers))
+ (ecase msg
+ (:read (rw:till (rw:peek-reader stream)))
+ (:write
+ (let ((rw.ui:*http-server*
+ (let ((pp (rw.http::post-parameters method body)))
+ (lambda (msg &rest args)
+ (declare (ignore args))
+ (ecase msg
+ (:method method)
+ (:post-parameters pp)))))
+ (*query-parameters* (nth-value 1 (rw.uri:parse-query-string query))))
+ (draw-zappel)))))
+
+(defun start ()
+ (rw.http:server "0.0.0.0" 2340 'zappel-handler :quit (lambda () nil)))
+
+;;(start)
+
+(defun save-image ()
+ #-sbcl
+ (error "TODO RW.DEMO.ZAPPEL::SAVE-IMAGE")
+ #+sbcl
+ (sb-ext:save-lisp-and-die "cl-rw-demo-zappel"
+ :executable t
+ :toplevel (lambda ()
+ (handler-case
+ (progn
+ (start)
+ (loop (sleep 1)))
+ (condition ()
+ (sb-ext:exit :code 1 :abort t))))))