テキストブラウザ #3 clispからncurses(Cライブラリ)を呼ぶ。

いろいろと挫折しながら、なんとか光が見えてきました。screenパッケージを見付けて、common lispの練習のために簡易テキストブラウザを作ろうとしましたが、screenパッケージを使っているとカーソル位置がずれてしまう問題が解決できずに諦めました。(id:smeghead:20070814:screen)そのままでは煮え切らないので、ncursesを調べていました。Cのライブラリ関数を呼出す方法とかも調べてなんとか、clispからncursesの関数が呼べるようになりました。もうこの時点で、最初の目的を考えると本末転倒です。いつものように、間違いとかおかしなところがあったら指摘していただけると嬉しいです。


Cの関数を呼出すのには、clispに付属しているffiパッケージがあったんですが、cffi で呼出すことにしました。

http://common-lisp.net/project/cffi/

ラッパーを書いてくれるコマンドswigを見付けました。

http://www.swig.org/Doc1.3/Lisp.html

本当は、ncurses/curses.h を使ってそのままラッパーを生成できると楽だったのですが、curses.hはマクロを使いまくっているせいか、生成に失敗していました。


回避策として、ダミーのプロトタイプ宣言を書いたファイルを用意して、ダミーのファイルをswigに喰わせることにしました。

dumy.h
WINDOW *   stdscr;
WINDOW *   curscr;
WINDOW *   newscr;
WINDOW *initscr(void);
int endwin(void);
int delwin(WINDOW *win);
WINDOW *newwin(int nlines, int ncols, int begin_y, int begin_x);
WINDOW *subwin(WINDOW *orig, int nlines, int ncols, int begin_y, int begin_x);
int move(int y, int x);
int refresh(void);
int wrefresh (WINDOW *);
int printw(char *fmt );
int wprintw (WINDOW *, const char *);

printwなどの可変長引数を取るCの関数は、可変長の部分は消してあります。

swig -cffi -module dumy dumy.h
実行結果
...
(cffi:defcvar ("stdscr" stdscr)
:pointer)
(cffi:defcvar ("curscr" curscr)
:pointer)
(cffi:defcvar ("newscr" newscr)
:pointer)
(cffi:defcfun ("initscr" initscr) :pointer)
(cffi:defcfun ("endwin" endwin) :int)
...

変数とか関数のラッパーが生成されるので、それをコピペして、curses-wrapper.lispを作りました。

curses-wrapper.lisp
(defpackage :curses-wrapper
(:use :common-lisp :cffi ))
(in-package :curses-wrapper)
(export '(wprintw initscr endwin subwin delwin
move printw refresh wrefresh
*stdscr* *curscr* *newscr*))
(define-foreign-library libncurses
(:unix (:or "cygncurses7.dll"  ; cygwin用
"cygncurses6.dll"  ; cygwin用
"cygncurses5.dll"  ; cygwin用
"libncurses.so"))
(t (:default "libncurses")))
(use-foreign-library libncurses)
(cffi:defcvar ("stdscr" *stdscr*)
:pointer)
(cffi:defcvar ("curscr" *curscr*)
:pointer)
(cffi:defcvar ("newscr" *newscr*)
:pointer)
(cffi:defcfun ("initscr" initscr) :pointer)
(cffi:defcfun ("endwin" endwin) :int)
(cffi:defcfun ("newwin" newwin) :pointer
(nlines :int)
(ncols :int)
(begin_y :int)
(begin_x :int))
(cffi:defcfun ("subwin" subwin) :pointer
(orig :pointer)
(nlines :int)
(ncols :int)
(begin_y :int)
(begin_x :int))
(cffi:defcfun ("delwin" delwin) :int
(win :pointer))
(cffi:defcfun ("move" move) :int
(y :int)
(x :int))
(cffi:defcfun ("printw" printw) :int
(fmt :string))
(cffi:defcfun ("refresh" refresh) :int)
(cffi:defcfun ("wprintw" wprintw) :int
(arg0 :pointer)
(arg1 :string))
(cffi:defcfun ("wrefresh" wrefresh) :int
(arg0 :pointer))

ようやく準備ができたので、ラッパーを使うコードを書きました。

test.lisp
(load "/usr/lib/clisp/asdf.lisp")
(setf asdf:*central-registry*
'(*default-pathname-defaults*
#p"/usr/lib/clisp/system/"))
(asdf:oos 'asdf:load-op :cffi :verbose nil)
(load "curses-wrapper.lisp")
(use-package :curses-wrapper)
(initscr)
(move 2 1)
(printw "from common lisp. ")
(refresh)
(setq messagebar (subwin *stdscr* 1 79 23 1))
(wprintw messagebar "hello, curses.")
(wrefresh messagebar)
(sleep 3)
(delwin messagebar)
(endwin)
実行結果
from common lisp.
hello, curses.

ふぅ。。ようやくcommon lispの練習のためのテキストブラウザを書く準備ができました。

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.