ラベル McCLIM の投稿を表示しています。 すべての投稿を表示
ラベル McCLIM の投稿を表示しています。 すべての投稿を表示

2010年10月25日月曜日

McCLIMで接続するディスプレイを選択する

McCLIMはX11プロトコルをしゃべるためにCLXを利用しています。なのでディスプレイ番号などを指定するのは最終的にはCLXの役割です。

McCLIMからCLXのopen-displayがどのように呼ばれるかを眺めることで、接続するディスプレイを選択する方法がわかったような気分になりました。

(asdf:oos 'asdf:load-op :mcclim)

(sb-posix:getenv "DISPLAY")
;;=> ":0.0"

;; .Xauthorityを読み込み、 ホスト名、ディスプレイ番号、プロトコルに対応する
;; :authorization-nameと:authorization-dataを取得する
(xlib::get-best-authorization "localhost" 0 :local)
;; =>"MIT-MAGIC-COOKIE-1"
;; =>#(161 76 219 58 93 240 175 179 37 197 235 248 55 5 32 117)

;; CLIMがこの関数を呼ぶ際の引数は、clx-portのserver-pathスロットに
;; セットされている。デフォルトだとcar部にキーワードシンボル:clxが、
;; cdr部に属性リストがセットされる。
;; clx-portオブジェクトはfind-port関数の中で作られる。
;; find-portはserver-pathをオプショナル引数とするが、デフォルトでは
;; *default-server-path*が渡されるので、この値を設定すると好きなディスプレイに接続できそう。
;; server-pathの第一要素はシンボルで、属性リストの:server-path-parserに関数が設定されている
;; 必要がある。この関数をserver-pathを引数として呼び出した返り値がclx-portにセットされる。
;; server-pathがnilの場合、find-default-server-pathの返り値を
;; server-pathとして利用する。おそらくcar部に:clxが入ったリストが返るので、:clxの属性リスト
;; に設定されている関数を変更することでも接続するディスプレイを選ぶことが出来ると思われる。

(funcall (get :clx :server-path-parser) '(:clx))
;; => (:CLX :HOST "" :DISPLAY-ID 0 :SCREEN-ID 0 :PROTOCOL :LOCAL)

2010年8月3日火曜日

McCLIMで升目を描く

formatting-tableを利用すれば良さそうだけど、無理やりな感じが現れてる。

LispworksのCLIMのページを参考にした。

(asdf:oos 'asdf:load-op :mcclim)
;;(asdf:oos 'asdf:load-op :mcclim-truetype)
(in-package :clim-user)

(defun output-table (&key (stream *standard-output*)
inter-row-spacing
inter-column-spacing)
(clim:formatting-table
(stream :x-spacing inter-row-spacing
:y-spacing inter-column-spacing)
(dotimes (i 3)
(clim:formatting-row
(stream)
(dotimes (j 3)
(clim:formatting-cell
(stream)
(clim:draw-rectangle* stream 10 10 50 50 :filled nil)))))))

(define-application-frame formatting-test-frame
()
()
(:menu-bar t)
(:panes
(app-pane :application
:min-width 150
:min-height 150
:scroll-bar t
:display-time :command-loop
:display-function #'(lambda (frame stream)
(output-table :stream stream :inter-row-spacing '(0 :line) :inter-column-spacing '(0 :line))))
)
(:layouts
(default (horizontally () app-pane))))

(define-formatting-test-frame-command (com-quit :menu t) ()
(frame-exit *application-frame*))

;;(run-frame-top-level (make-application-frame 'formatting-test-frame))

2010年7月18日日曜日

McCLIMでグラフを書く

McCLIMでグラフを描画する。ノードが循環すると繰り返し処理をしようとして落ちるようだ。

(require :asdf)
(asdf:oos 'asdf:load-op :mcclim)

(in-package :clim-user)

(define-application-frame graph-frame ()
()
(:menu-bar t)
(:panes
(app :application
:min-width 200
:min-height 200
:scroll-bars nil
:display-time :command-loop
:display-function 'draw))
(:layouts
(default (horizontally () app))))

(define-graph-frame-command (com-quit :menu t) ()
(frame-exit *application-frame*))

(defstruct node (name "") (children nil))
(defparameter gl (let* ((2a (make-node :name "2A"))
(2b (make-node :name "2B"))
(2c (make-node :name "2C"))
(1a (make-node :name "1A" :children (list 2a 2b)))
(1b (make-node :name "1B" :children (list 2b 2c))))
(make-node :name "0" :children (list 1a 1b))))
(define-presentation-type node ())
(defun test-graph (root-node &rest keys)
(apply #'clim:format-graph-from-root gl
#'(lambda (node stream)
(clim:surrounding-output-with-border (stream :shape :underline)
(write-string (node-name node) stream)))
#'node-children
keys))
(defgeneric draw (frame stream))
(defmethod draw ((frame application-frame) stream)
(declare (ignore frame))
(test-graph gl :stream stream))

(defun run ()
(clim:run-frame-top-level (clim:make-application-frame 'graph-frame)))

McCLIMはPostScriptを出力する事もできるらしい。

(defun output-postscript (filename)
(with-open-file (out filename :direction :output)
(with-output-to-postscript-stream
(stream out
:header-comments '(:title "PostScript Test"))
(test-graph gl :stream stream))))

2010年2月6日土曜日

McCLIMでライフゲーム

McCLIMでライフゲームをやってみた。描画が遅いのでどうしようかと調べてみたところ、 with-output-bufferedとclimi::with-double-buffering というそれっぽいのが見つかった。

with-double-bufferingはエクスポートされていないので、とりあえずは wiht-output-bufferedを使った。

(require :asdf)
(asdf:oos 'asdf:load-op :mcclim)
(asdf:oos 'asdf:load-op :portable-threads)

(in-package :clim-user)
(defparameter +block-size+ 10)
(defparameter +field-size-x+ 10)
(defparameter +field-size-y+ 10)

(defun draw (frame stream)
(let ((f (field frame))
(medium (sheet-medium stream)))
(clim:with-output-buffered (medium)
;; (climi::with-double-buffering ((stream 0 0 500 500) (wtf))
(dotimes (i (array-dimension f 0))
(dotimes (j (array-dimension f 1))
(draw-rectangle* medium
;; (draw-rectangle* stream
(* i +block-size+) (* j +block-size+)
(* (1+ i) +block-size+) (* (1+ j) +block-size+)
:ink (if (= 1 (aref f i j)) +black+ +white+)))))
(medium-finish-output medium)
(clim:medium-force-output medium)
))

(defun update-field (field tmp)
(let ((x-limit (array-dimension tmp 0))
(y-limit (array-dimension tmp 1)))
(dotimes (i x-limit)
(dotimes (j y-limit)
(setf (aref tmp i j)
(next field i j x-limit y-limit)))))
tmp)

(defun next (field x y x-limit y-limit)
(case (- (loop
:for i
from (if (= x 0) 0 -1)
to (if (= x (1- x-limit)) 0 1)
:sum (loop
:for j from (if (= y 0) 0 -1)
to (if (= y (1- y-limit)) 0 1)
:sum (aref field (+ x i) (+ y j))))
(aref field x y) 1 0)
((3) 1)
((2) (if (aref field x y) 1 0))
(T 0)))

(define-application-frame lifegame-frame ()
((field :accessor field :initform nil)
(tmp-field :accessor tmp-field :initform nil)
(timer-process :accessor timer-process :initform nil))
(:menu-bar t)
(:panes
(canvas :application
:min-width 200
:min-height 200
:scroll-bars T
:display-time :command-loop
:display-function 'draw))
(:layouts
(default (horizontally () canvas))))

(define-lifegame-frame-command (com-quit :menu t) ()
(frame-exit *application-frame*))

(define-lifegame-frame-command (com-update :menu t) ()
(setf (tmp-field *application-frame*)
(update-field (field *application-frame*)
(tmp-field *application-frame*)))
(rotatef (tmp-field *application-frame*)
(field *application-frame*))
(redisplay-frame-panes *application-frame*))

(defun init-field (field x y)
(dotimes (i x)
(dotimes (j y)
(setf (aref field i j)
(if (< (random 10) 7)
0 1))))
field)

(defclass timer-event (device-event)
()
(:default-initargs :modifier-state 0))

(defmethod handle-event ((client application-pane) (event timer-event))
(com-update))

(defmethod run-frame-top-level ((frame lifegame-frame) &key)
(let ((tls (frame-top-level-sheet frame))
(canvas (get-frame-pane frame 'canvas)))
(format t "spawn-thread~%")
(setf (timer-process frame)
(portable-threads:spawn-thread
"timer"
#'(lambda ()
(loop
:do
(sleep 1.0)
(queue-event tls (make-instance 'timer-event :sheet canvas))
))))
(call-next-method)
(format t "return from call-next-method~%")
(when (timer-process frame)
(portable-threads:kill-thread (timer-process frame)))))

(defun run (&optional (x +field-size-x+) (y +field-size-x+))
(let ((f (make-array (list x y) :initial-element 0))
(tmp (make-array (list x y) :initial-element 0))
(frame (make-application-frame 'lifegame-frame)))
(init-field f x y)
(setf (field frame) f)
(setf (tmp-field frame) tmp)
(run-frame-top-level frame)))

;;(run 60 50)

2010年2月5日金曜日

McCLIMで時計っぽいもの

Common LispのGUIライブラリといえばMcCLIMがある。日本語資料の少なさとか他のGUIライブラリとの差異とかはご愛嬌というやつでしょう。

このMcCLIM、ユーザインターフェースを作るときは良いけれど、一定時間ごとに再描画したい、というようなユーザの動作が絡まないときの処理をどう書けば良いかよくわからない。

一定時間ごとにイベントを発生させられれば良いのだけど、よくわからないので他にスレッドを作ってそちらに任せることで解決しようとしてみた。

(require :asdf)
(asdf:oos 'asdf:load-op :mcclim)
(asdf:oos 'asdf:load-op :portable-threads)

(in-package :clim-user)

(defun draw (frame stream)
(declare (ignore frame))
(multiple-value-bind
(sec min hour) (get-decoded-time)
(let ((sec-rad (* 2 pi (/ (- (* sec 6)90) 360)))
(min-rad (* 2 pi (/ (- (* min 6)90) 360)))
(hour-rad (* 2 pi
(/ (- (+ (* hour 30) (/ min 2)) 90)
360))))
(format stream "~{~a~^:~}" (list hour min sec))
(draw-line* stream 100 100
(+ 100 (* 30 (cos sec-rad)))
(+ 100 (* 30 (sin sec-rad)))
:ink (make-rgb-color 0.0 1.0 0.0))
(draw-arrow* stream 100 100
(+ 100 (* 30 (cos min-rad)))
(+ 100 (* 30 (sin min-rad)))
:ink (make-rgb-color 0.0 0.0 1.0))
(draw-arrow* stream 100 100
(+ 100 (* 20 (cos hour-rad)))
(+ 100 (* 20 (sin hour-rad)))
:ink (make-rgb-color 0.0 0.0 1.0))
(draw-circle* stream 100 100 30
:filled nil
:ink (make-rgb-color 1.0 0.0 0.0)))))

(define-application-frame clock-frame ()
((clock-process :accessor clock-process :initform nil)) ;slots
(:menu-bar t)
(:panes
(canvas :application
:min-width 200
:min-height 200
:scroll-bars nil
:display-time :command-loop
:display-function 'draw))
(:layouts
(default (horizontally () canvas))))

(define-clock-frame-command (com-quit :menu t) ()
(frame-exit *application-frame*))

(defclass redraw-clock-event (device-event)
()
(:default-initargs :modifier-state 0))

(defmethod handle-event ((client application-pane) (event redraw-clock-event))
(format t "handle-event(redraw)~%")
(with-application-frame (frame)
(redisplay-frame-pane frame client)))

(defmethod run-frame-top-level ((frame clock-frame) &key)
(let ((tls (frame-top-level-sheet frame))
(canvas (get-frame-pane frame 'canvas)))
(format t "spawn-thread\n")
(setf (clock-process frame)
(portable-threads:spawn-thread
"clock"
#'(lambda ()
(loop
:do
(sleep 0.5)
(queue-event tls (make-instance 'redraw-clock-event :sheet canvas))
))))
(format t "~a\n" (clock-process frame))
(call-next-method)
(format t "return from call-next-method~%")
(when (clock-process frame)
(portable-threads:kill-thread (clock-process frame)))))

(defun run ()
(run-frame-top-level
(make-application-frame 'clock-frame)))

;;(run)

paneの:display-timeあたりをどうにかするとうまいことできたりするのだろうか。