Kouhei Sutou
kou****@cozmi*****
2006年 1月 3日 (火) 19:07:02 JST
須藤です. # 注: 長いです. 風博士をスクリプト言語から拡張するのを楽にするための機構を導 入しました.風博士とスクリプト言語間のブリッジは風博士起動時 に(GModuleを使って)動的にロードするようにしてあるので,後 から拡張を付け加えたり削除したりすることができます. 風博士とスクリプト言語はアクション(GtkAction)経由で連携す ることになると思います.つまり,こんな風です. 風博士起動 ↓ スクリプト言語とのブリッジのロード ↓ ブリッジの初期化関数の起動 ↓ ブリッジの初期化関数内で初期化関数に渡されたKzWindowの actionsメンバ(っていうんだっけ?)にアクションを追加 ↓(この時点でスクリプト言語で作成したアクションが使用可能) 風博士本体側でkz-ui-*.xmlを読み込んでUIとアクションを結び つける. あるいは, ユーザがマウスジェスチャの設定でスクリプト言語で作成した アクションを起動できるようにする. また, 風博士本体内部でスクリプト言語によって作成したアクションを 使用する. 一応,手元には(というか後輩に書いてもらった)Rubyで拡張する ためのコードがありますが,ちょっと訳あってまだコミットしてい ません.他の言語で拡張したいという人がいたら相談にのるので聞 いてください. 例えば,Rubyで風博士を拡張するためには以下のようにします. 1. configure.inにRubyを検出するコードを記述. エラー検出無し(rubyがインストールされていないなど)だとこん な感じで十分でしょう. dnl ************************************************************** dnl Check for Ruby. dnl ************************************************************** AC_PATH_PROG(RUBY, ruby, none) RUBY_CFLAGS="-I`$RUBY -rrbconfig -e 'print Config::CONFIG.fetch(%q(archdir))'` " RUBY_CFLAGS="$RUBY_CFLAGS -I`$RUBY -rrbconfig -e 'print Config::CONFIG.fetch(%q(sitearchdir))'` " RUBY_CFLAGS="$RUBY_CFLAGS `$RUBY -rrbconfig -e 'print Config::CONFIG.fetch(%q(CFLAGS))'`" RUBY_LIBS="`$RUBY -rrbconfig -e 'print Config::CONFIG.fetch(%q(LIBRUBYARG))'` " RUBY_LDFLAGS="-L`$RUBY -rrbconfig -e 'print Config::CONFIG.fetch(%q(libdir))'`" RUBY_EXT_LDFLAGS="-shrext .`$RUBY -rrbconfig -e 'print Config::CONFIG.fetch(%q(DLEXT))'`" AC_SUBST(RUBY_CFLAGS) AC_SUBST(RUBY_LIBS) AC_SUBST(RUBY_LDFLAGS) AC_SUBST(RUBY_EXT_LDFLAGS) 2. configure.inに新しく作るディレクトリ用のMakefileを生成す る設定を追加. 一番下の方にある AC_CONFIG_FILES([ ... ]) の中に追加します.とりあえず,ext/#{言語名}とdata/ext/#{言語名} というディレクトリ用を作っておけばよいでしょう. 今回は#{言語名}をrubyとします. AC_CONFIG_FILES([ ... ext/ruby/Makefile data/ext/ruby/Makefile ... ]) 今,私が考えているext/とdata/ext/の違いは以下の通りです. ext: 風博士とスクリプト言語をつなぐCで書かれたコード用ディ レクトリ(俗に言うバインディング用のディレクトリ) data/ext: スクリプト言語で書かれた風博士を拡張するためのス クリプト用ディレクトリ(Rubyなら*.rbを置く) 3. 風博士とスクリプト言語をつなぐ部分を作る 3.1 ext/Makefile.amにext/ruby/以下をコンパイル対象にするよう な記述を追加 ext/Makefile.amのSUBDIRS変数にrubyを追加します. SUBDIRS = ruby 3.2 ext/ruby/Makefile.amを作る 大事なことは風博士とスクリプト言語をつなぐブリッジモジュール は * $(extdir)/#{言語名}/以下に * libkezext.soという名前で作り, * void kz_ext_init(KzWindow *kz)という初期化ファイルを externしている ということです. #{言語名}がrubyでの場合は,こんな感じになるでしょう. AM_CPPFLAGSとかINCLUDESはsrc/Makefile.amから持ってきたやつに スクリプト言語用の設定を少し追加しているだけです.また,この 例ではブリッジモジュールはkz-rb-ext.cに書くようにしています. # -*- Mode: Makefile; tab-width: 8; indent-tabs-mode: t; -*- extrubydir = $(extdir)/ruby extruby_LTLIBRARIES = libkzext.la AM_CPPFLAGS = \ -DLOCALEDIR=\""$(localedir)"\" \ -DSYSCONFDIR=\""$(sysconfdir)"\" \ -DDATADIR=\""$(datadir)"\" \ -DEXTDIR=\""$(extdir)"\" \ -DKZ_SYSCONFDIR=\""$(sysconfdir)/$(PACKAGE)"\" \ -DKZ_DATADIR=\""$(datadir)/$(PACKAGE)"\" \ -DKZ_RUBY_EXTDIR=\""$(extrubydir)"\" \ -DGTK_DISABLE_DEPRECATED=1 \ -DGDK_DISABLE_DEPRECATED=1 \ -DG_DISABLE_DEPRECATED=1 INCLUDES = \ $(MOZILLA_COMPONENT_CFLAGS) \ $(GTK_CFLAGS) \ $(LIBGNUTLS_CFLAGS) \ $(RUBY_CFLAGS) \ $(RUBY_EXT_CFLAGS) \ -I$(top_srcdir)/src \ -I$(top_srcdir)/src/actions \ -I$(top_srcdir)/src/bookmarks \ -I$(top_srcdir)/src/mozilla \ -I$(top_srcdir)/src/libegg/pixbufthumbnail \ -I$(top_srcdir)/src/libegg/regex \ -I$(top_srcdir)/src/libegg/md5 \ -I$(top_srcdir)/src/net \ -I$(top_srcdir)/src/sidebar \ -I$(top_srcdir)/src/utils \ -I$(top_srcdir)/src/widget libkzext_la_SOURCES = \ kz-rb-ext.c libkzext_la_LDFLAGS = \ $(RUBY_LDFLAGS) \ $(RUBY_EXT_LDFLAGS) libkzext_la_LIBADD = \ $(INTLLIBS) \ $(GTK_LIBS) \ $(LIBGNUTLS_LIBS) \ $(RUBY_LIBS) \ $(MOZILLA_COMPONENT_LIBS) \ $(top_builddir)/src/libkazehakase.la 3.3 風博士とスクリプト言語をつなぐ部分をCで書く ext/ruby/kz-rb-ext.cはこんな風になるでしょう. # 本当はもっと気合いを入れて書かなければいけません. #include <rbgtk.h> #undef PACKAGE_NAME #undef PACKAGE_TARNAME #undef PACKAGE_STRING #undef PACKAGE_VERSION #include "kz-window.h" extern void kz_ext_init(KzWindow *kz); #define INIT_RB "kazehakase" #define _SELF(obj) (KZ_WINDOW(RVAL2GOBJ(obj))) static ID id_default; static VALUE rb_kz_window_set_default(VALUE self, VALUE window) { rb_cvar_set(self, id_default, window, Qfalse); return Qnil; } static VALUE rb_kz_window_get_default(VALUE self) { return rb_cvar_get(self, id_default); } static VALUE rb_kz_window_actions(VALUE self) { return GOBJ2RVAL(_SELF(self)->actions); } static void init_kz(KzWIndow *kz) { VALUE mKz, cKzWindow; mKz = rb_define_module("Kz"); id_default = rb_intern("@@default"); cKzWindow = G_DEF_CLASS(KZ_TYPE_WINDOW, "Window", mKz); rb_define_singleton_method(cKzWindow, "set_default", rb_kz_window_set_default, 1); rb_alias(rb_singleton_class(cKzWindow), rb_intern("default="), rb_intern("set_default")); rb_define_singleton_method(cKzWindow, "default", rb_kz_window_get_default, 0); rb_define_method(cKzWindow, "actions", rb_kz_window_actions, 0); cKzWindow = rb_const_get(mKz, rb_intern("Window")); rb_funcall(cKzWindow, rb_intern("set_default"), 1, GOBJ2RVAL(kz)); } void kz_ext_init (KzWindow *kz) { VALUE mGtk; VALUE stack_start; gchar *dirname; char *argv[] = {"kazehakase"}; ruby_init(); Init_stack(&stack_start); ruby_init_loadpath(); ruby_script("kazehakase"); ruby_set_argv(1, argv); dirname = g_build_filename(KZ_DATADIR, "ext", "ruby", NULL); rb_ary_unshift(rb_load_path, rb_str_new2(dirname)); g_free(dirname); rb_funcall(Qnil, rb_intern("require"), 1, rb_str_new2("gtk2")); init_kz(KzWindow *kz); mGtk = rb_const_get(rb_cObject, rb_intern("Gtk")); rb_funcall(mGtk, rb_intern("init"), 0); rb_funcall(Qnil, rb_intern("require"), 1, rb_str_new2(INIT_RB)); } 4. 風博士を拡張する部分をスクリプト言語で書く 4.1 data/ext/Makefile.amにdata/ext/ruby/以下をコンパイル対象 にするような記述を追加 data/ext/Makefile.amのSUBDIRS変数にrubyを追加します. SUBDIRS = ruby 4.2 data/ext/ruby/Makefile.amを作る 大事なことはスクリプトは * $(datadir)/$(PACKAGE)/ext/#{言語名} にインストールするということです. # そうしなくても動くようにはできるんですが. 例えばこんな感じです. # -*- Mode: Makefile; tab-width: 8; indent-tabs-mode: t; -*- extrubydir = $(datadir)/$(PACKAGE)/ext/ruby extruby_DATA = \ kazehakase.rb EXTRA_DIST = \ $(extruby_DATA) 4.3 風博士を拡張するプログラムをスクリプト言語で書く ブリッジモジュールでロードするようにしたkazehakase.rbに例え ば以下のように書きます. act = Gtk::Action.new("Hello", "Hello Wolrd", "print Hello World", nil) act.signal_connect("activate") {puts "Hello World"} Kz::Window.default.actions.add_action(act) 5 確認する 風博士を起動して「編集」→「設定」→「ジェスチャ」でアクショ ンの中に「Hello」アクションがないか探してみてください.あっ たら適当にジェスチャを設定して実行してみてください.風博士を 起動したコンソールに"Hello World"と出力されたら成功です. 6. おまけ 風博士をインストールせずに実験したい場合は以下のようにすると 便利です. % ./configure --sysconfdir=$PWD/dummy-etc --datadir=$PWD/dummy-share --libdir=$PWD/dummy-lib % mkdir -p dummy-etc % ln -s ../etc dummy-etc/kazehakase % mkdir -p dummy-share % ln -s ../data dummy-share/kazehakase % mkdir -p dummy-lib/kazehakase/ext % ln -s ../../../ext/ruby/.libs dummy-lib/kazehakase/ext/ruby % make % src/kazehakase