termeの操作関数を作る
以前、readline / editlineの依存から脱出しようと、自前のプロンプトを作成しました。
その機能に、termeモジュールという名前を付けました。
何も考えずにnptをコンパイルすると標準で使えるようになっています。
どうやって実現したのかというと、端末をraw modeにしてエスケープシーケンスをまともに扱っただけです。
あまりnptに機能を盛り込みたくなかったのですが、せっかく作ったので、端末の機能が使えるような関数を用意したら面白いのでは?
ということでやってみました。
関数はnpt-system:terme
というものに集約されています。
たった一つの関数にまとめています。
nptはプロセスの起動を早くしたいため、極力関数を作りたくないのです。
あとでちゃんとしたドキュメントを用意しますが、適当な使い方を書いておきます。
書いておかないと自分が忘れますので。
★用意しました! Lisp関数仕様 - terme
まずはterme
を使えるようにします。
面倒なのでnpt-system
パッケージをuseします。
(use-package 'npt-system)
端末をraw modeにします。
(terme 'terme-begin) ;; ★注意 -> #<PAPER ...>
返却値は変更前の端末情報を格納したPaperオブジェクトです。
「★注意」と書かれているように結構危険な命令であり、
raw modeに移行すると以降の表示がめちゃくちゃになってしまします。
終わるときは必ずterme-end
を実行するようにしてください。
(terme 'terme-end paper)
典型的な使用例は下記のようになります。
(let ((paper (terme 'terme-begin))) (unwind-protect (progn ...) (terme 'terme-end paper)))
(progn ...)
内では、入力、出力、文字種の変更、色の変更などができます。
format
関数などは使えますが、出力はめちゃくちゃになりますのでお勧めしません。
最初にやることは、まずどうやって終了させるかです。
raw modeなので、Ctrl+C
やCtrl+Z
などを押しても入力として扱われるため反応しません。
キーの入力は、terme-input
によって行われます。
まずはCtrl + C
を補足する方法を説明します。
(multiple-value-bind (type value) (terme 'terme-input) (when (and (eq type 'terme-code) (eql value 3)) ;; ★Ctrl + Cが押された ))
出力は次のようになります。
(terme 'terme-output x)
x
は、文字、文字列、数値、nil
のどれかです。
文字はそのまま出力。
文字列もそのまま出力。
数値はUnicodeとして出力。
そしてnil
は、いわゆるfinish-output
です。
x
は省略可能であり、そのときはnil
になります。
覚えておかなければならないことがあり、
文字の出力の他にも、カーソル移動だったり、画面全消去などの操作があるのですが、
機能は全てエスケープシーケンスを文字として出力して実現しています。
よって、finish-output
である、(terme 'terme-output)
を実行しない限り、
おそらくは画面に反映されません。
例をあげます。
カーソルを座標(3, 5)に移動する例は次のようになります。
(terme 'terme-move 3 5 :absolute)
もし即座に画面に反応させたいのであれば、次のように実行してください。
(terme 'terme-move 3 5 :absolute) (terme 'terme-output)
ここまでわかれば使えると思います。
実行例を示します。
(use-package 'npt-system) (defvar *x*) (defvar *y*) (defvar *cx*) (defvar *cy*) (defvar *rx*) (defvar *ry*) (defvar *i*) (defvar *char*) (defconstant pi2 (* 2.0d0 pi)) (defun set-position () (let* ((i (mod *i* 360)) (theta (float (/ (* pi2 i) 360.0d0))) (x (* *rx* (sin theta))) (y (* *ry* (cos theta)))) (setq *x* (truncate (+ *cx* x))) (setq *y* (truncate (+ *cy* y))) (when (zerop i) (setq *char* (not *char*))))) (defun main-output () (let ((c (if *char* #\+ #\Space))) (set-position) (terme 'terme-font 'palback (mod *i* #xFF)) (terme 'terme-move *x* *y* :absolute) (terme 'terme-output c) (terme 'terme-move 0 0 :absolute) (terme 'terme-output))) (defun main-input () (multiple-value-bind (type value) (terme 'terme-input 0.01) (when (and (eq type 'terme-code) (eql value 3)) (return-from main-input nil)) (main-output) (incf *i* 1)) t) (defun main-loop () (terme 'terme-clear) (terme 'terme-output) (multiple-value-setq (*x* *y*) (terme 'terme-size)) (setq *i* 1) (setq *cx* (* *x* 0.5d0)) (setq *cy* (* *y* 0.5d0)) (setq *rx* (* *cx* 0.9d0)) (setq *ry* (* *cy* 0.9d0)) (setq *char* t) (loop while (main-input))) (defun main-clean (x) (terme 'terme-font nil) (terme 'terme-end x)) (defun main () (let ((x (terme 'terme-begin))) (unwind-protect (main-loop) (main-clean x)))) (main)
終了はCtrl + C
を押してください。
実行画面は次の通り。
色がついてると綺麗ですね。
大昔、狂ったようにBASICをやっていた時代がありました。
家になぜか当時でも時代遅れの8bitのパソコンがあって、
何を作るわけでもなく、画面に文字を出して、
キーボードで移動させたりして遊んでいたのですが、
そんな時代を思い出してしまいました。
A$=INKEY$
だったかな。
不思議なことにあまり覚えてません。