Hatena::ブログ(Diary)

技術日記@kiwanami

2011-07-01

GNOME Do の PPA を作った

前回、GNOME Do のmigemo対応をやってみたのですが、ついでなのでPPAでパッケージを作ってみました。

Ubuntu Maverick(10.10) と Natty(11.04) の人はこれを使うと簡単に導入できると思います。

導入方法

以下のコマンドを入力します。オレオレ認証のkiwanami鍵とPPAリポジトリの場所が入りますので、信用できない人はやめておきましょう。(2011/07/02 訂正 apt-key を書き間違えていましたので修正しました。@mori_devさんありがとうございます。)

$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4009F8ED 26D9017F
$ sudo add-apt-repository ppa:m-sakurai/ppa1
$ sudo apt-get update

後は、apt-get でインストールするだけです。もちろん、synapticやソフトウエアセンターで入れてもかまいません。

$ sudo apt-get install gnome-do

依存パッケージとして cmigemo も入ります。 cmigemo の辞書は /usr/share/migemo に入りますので、Emacs 等で設定して使うことも出来ます。

既に入っている人は、おそらくアップグレードするかどうか尋ねられると思いますので、アップグレードするとPPA の GNOME Do が入ると思います。

GNOME Do 自体のバージョン表記は変更していないので、ちゃんと入ったかどうか確認するには「kagi」や「settei」とか入力してみてください。日本語がヒットしていればmigemoが動いています。

Ubuntu(debian)パッケージについて

以下は作業のメモなどです。debian パッケージは初めて作ったのですが、やはり各種多様なビルドをこなすだけあって、なかなか最初は理解が難しかったです。多分、経験者に師事して習ったりつっこんでもらいながら慣れていくものなのかなと思いました。しかしながら、理解していくうちにパッケージやlaunchpadの仕組みが非常に良くできていると思いました。

参考にした情報

検索するとそれなりに情報が見つかったのですが、debianパッケージ自体が進化していたり、ツールやパッケージの作り方が複数存在するため、どこを見ても違うことが書いてあるように感じました。結局、本家の情報を読むことが一番だったような気がします。

  • The Ubuntu Packaging Guide 日本語版
    • Ubuntuパッケージの本家ドキュメントの日本語訳。
    • Hardy頃の情報で古いらしいけども一番参考になった。
    • ここで dh_make は devhelper だと言うことが分かった。CDBSはまだよく分からない。
    • こっちは dpatch を説明していて、ここでやっとパッチの管理方法にもいくつか方法があることが分かった。quiltが楽。
  • PbuilderHowto - Ubuntu Wiki
    • pbuilder で Universe や PPA を使う方法が分からなかったので調べた
    • pbuilder すごいというか豪快

憩いの場さんのPPAにも cmigemo があったことを先ほど知りました。。。

作業メモ

以下は適当な作業メモです。突っ込み所があれば是非教えてください。

C/Migemo

ダウンロードと下準備

$ wget http://cmigemo.googlecode.com/files/cmigemo-default-src-20110227.zip
$ unzip cmigemo-default-src-20110227.zip
$ mv cmigemo-default-src cmigemo-20110227
$ tar zcvf cmigemo-20110227.tar.gz cmigemo-20110227

dh_make する

$ tar zxvf cmigemo-20110227.tar.gz
$ cd cmigemo-20110227
$ dh_make -e (mail address) -f ../cmigemo-20110227.tar.gz
 -> s / single を選ぶ

PPA なので省略

$ cd debian
$ rm -f README.Debian README.source *.ex *.EX

changelog, control, copyright, rules を編集する。

パッチを作る

$ cd ..
$ dquilt new fix-limit.patch
$ dquilt add src/wordbuf.c
$ vi src/wordbuf.c
   -> include <limits.h>
$ dquilt refresh
$ dquilt header -e
   -> fix an undefined symbol error.
$ dquilt new fix-makes.patch
$ dquilt add dict/dict.mak
$ vi dict/dict.mak
   -> gcc-dict: + utf-8
$ dquilt add Makefile
$ vi Makefile
   -> default: gcc-all
   -> install: gcc-install
$ dquilt add config.mk
$ vi config.mk
   -> /usr/local -> $(DESTDIR)/usr
   -> qkc -> nkf
$ dquilt refresh
$ dquilt header -e
   -> migemo patch

パッケージをビルドする

$ debuild -S -sa -k4009F8ED
$ sudo pbuilder build ../cmigemo_20110227-1.dsc

問題なければ dput でアップする。

GNOME Do

Maverick版は dpatch でパッチを作ってるので、既に入っているパッチの続きでパッチ作成。

  • migemo の辞書の場所を修正
  • 20_add_migemo.dpatch

ちなみにNatty版はquiltに変わっていた。

  • control の修正
    • Maintainer を自分に変更
    • cmigemo を依存に追加
    • ドキュメントを少し更新
  • copyright に追加
  • changelog に追加
    • version を修正
      • Maverick : 0.8.3.1+dfsg-2ubuntu1-migemo1
      • Natty : 0.8.4-0ubuntu2-migemo2

.pbuilderrc に PPA 追加してパッケージビルド。良ければ dput。

dput してビルド失敗したら、修正してバージョンを上げてアップし直す。

2011-06-12

GNOME Do を migemo 対応にしてみた

GNOME Do というランチャーみたいなものがあります。

GNOME 上のただのランチャーであれば、標準の Alt+F2 でも悪くないのですが、 GNOME Do は Plugin によって Emacs の Anything のように拡張できると言うところがポイントです。最近開発が止まり気味ですが、端で見ている限りは、十分枯れていて、もうやることが無くなってきたと言う風に見えます。

大変便利なのですが、とても残念なことに検索に日本語が(ほとんど)使えません。また、日本語が使えない上に L10N な日本語アプリ名で検索しようとするため、ほとんどのアプリGNOME Do から起動できません。自分は、どうしても GNOME Do から起動したいものは、 /usr/share/applications/ の中に入っているショートカットファイルをいちいち書き換えて使っていました。

アルファベットに最適化されたヒューリスティックなスコアリングアルゴリズムを使っており、さらにキー入力イベントで処理しているため、以前コードを調べたときには日本語化は無理かと思っていました。一応多言語対応として簡単な InputMethod の対応は入っているのですが、ほとんどの環境でうまく動かなかったり、動いたとしても変換の手間があるため、GNOME Do の素早く選んで実行するという良さが生かすことができません。

最近、CMigemoがC#から使えると知っていろいろ実験してみたところ、 Linux 上の Mono でも使えることが分かりました。ということで、強引に GNOME Do に組み込んでみました。結果、激しく便利になりました。

アプリケーションだけでなくあらゆるプラグインmigemo で検索可能になるため、設定、コンタクトリストや各種ファイル、PDFや楽曲名など、素早く絞り込んで開くことが出来ます。これで一気に生活が楽になりました。今まで Emacs の Anything でやっていたことが GNOME Do で出来るようになったため、早くも anything-books.el の地位が危うくなりました。

@kaoriya さんありがとうございます!!! あと、こういうことが簡単にできる Mono すごい!!!


以下、その作業のメモです。興味がある方は at your own risk でどうぞ。

migemo 対応作業メモ

Ubuntu 10.10 amd64 上での作業です。 11.04 でも人間パッチで同様に出来ると思います。

CMigemoのコンパイルインストール

2011/06/23 追記:不正な正規表現で落ちるバグがありましたので修正しました。kazuさんご連絡ありがとうございます。)

下のサイトから、最新版ソースをダウンロードする。

$ unzip cmigemo-default-src-20110227.zip
$ cd cmigemo-default-src/
$ chmod u+x configure
$ ./configure
$ vi src/wordbuf.c
→ ヘッダーに #include <limits.h> を加える
$ make gcc
$ vi dict/dict.mk
→ gcc の項目で、 euc-jp に加えて utf-8 を追加
$ make gcc-dict
$ sudo make gcc-install

この中の tools/Migemo.cs を後で使うのでまだ消さない。

GNOME Do の修正、インストール

修正内容は https://gist.github.com/1021707 にまとめてあります。(2011/06/23 パッチ内容修正)

$ cp (さっきの cmigemo のディレクトリ)/tools/Migemo.cs .
$ wget https://raw.github.com/gist/1021707/0a8739105314a4d9f14d5956f01e4c90d4c40e77/gnome-do_0.8.3.1+dfsg-2ubuntu1-migemo.patch
$ sudo apt-get build-dep gnome-do
$ apt-get source gnome-do
$ ORGDIR=gnome-do-0.8.3.1+dfsg
$ cp Migemo.cs $ORGDIR/Do.Platform/src/Do.Platform/Migemo.cs  #2011/06/23 訂正
$ cd $ORGDIR
$ patch -u -p1 < ../gnome-do_0.8.3.1+dfsg-2ubuntu1-migemo.patch
$ dpkg-buildpackage -rfakeroot -uc -b
$ cd ..
$ sudo dpkg -i gnome-do_0.8.3.1+dfsg-2ubuntu1_amd64.deb

多分、これでコンパイルが通ってインストールできると思います。

ポイントは、 cmigemo の最新版を使って、さらに辞書を UTF-8 にすることです。

PPA とかにすると良いのかもしれませんが、ライセンス的に Migemo.cs をどうしていいのかよく分からなかったので、要望があれば考えようと思っています。

2011-05-14

LinuxでiPadをセカンドモニターにする

はじめに

「iPad」と呼ばれるディスプレイ装置はすごいデバイスです。タッチパネル付きの9.7インチIPS液晶で、解像度はXGA、しかも無線でマシンと接続できるだけでなく、わずか700g弱という重さながら、バッテリーを内蔵していて10時間以上の連続動作が可能です。

AirDisplayというソフトを使うと、簡単にMacやWindowsでiPadをセカンドディスプレイにすることが出来ます。

残念ながら、このiPadをLinuxでセカンドディスプレイとして使う情報はありません。いろいろ検索してみましたが、やっている人は誰もいないようです。

そこでiPadをLinuxでもセカンドモニターにしてみる方法を探してみました。結果としては一応セカンドモニターとして使えなくもないことが分かりました。

達成目標

ここでの「セカンドモニター」の定義ですが、iPadのAirDisplayと同じような動作を考えています。具体的には以下のようです。

  • 機能要件(目ため、動作)
    • ポインターがメインのディスプレイから連続的に移動できる
    • 表示中のウインドウも連続的に移動できる
    • ディスプレイの相対位置(上下左右)も自由に設定できる
  • 非機能要件(使い勝手)
    • 必要なときに動的につけたり外したり出来る
      • X再起動無しでディスプレイを拡張できる
      • 逆にディスプレイを停止してもアプリが死なない
    • 現在の GNOME や compiz の設定を変えたくない
前提

とりあえずは現在の自分のマシンで動くことです。

すべてVNCのポートは5901を仮定しています。

今回試した方法は一般的なハックですので、おそらくXに詳しい人なら他の環境でも容易に実現できると思います。

あと、VNCで画面を転送していますので画面の動きがすごく遅いです。動画とかは全く無理です。シェルの操作がまあまあ我慢できるぐらいの速度です。(追記 2011/05/15)

方法1: XINERAMA + x11vnc

概要

まずはXのディスプレイ拡張技術:XINERAMAを使ってを素直に拡張する方法です。dummyドライバで見えない仮想スクリーンを作って、XINERAMAで画面をつなげます。仮想スクリーンはVNC(x11vnc)でiPad側に表示させます。

XINERAMA + x11vnc

  • 良いところ
    • すべての機能要件を満たす
  • 問題
    • Xの設定変更にはXの再起動が必要
    • xrandr, compiz が使えない
インストール

必要なもの:

  • xserver-xorg-video-dummy
  • x11vnc

x11vnc は synaptic や apt-get で入ります。

問題は xserver-xorg-video-dummy です。これをパッケージから入れようとすると、現在入っている xserver-xorg-video〜 関係が軒並み削除されようとしてしまいます。ディスプレイのない環境で使うものというパッケージとして定義されているのかも知れません。さらにコードにバグも残っていますので、以下のように手動で修正して入れる必要があります。(XorgのログにWindowTableが無いといったエラーが出る場合はこの修正が必要です)

$ sudo apt-get build-dep xserver-xorg-video-dummy
$ apt-get source xserver-xorg-video-dummy
$ cd xserver-xorg-video-dummy-0.3.3
$ vi src/dummy_driver.c
  (下の修正箇所を参照)
$ dpkg-buildpackage -rfakeroot -uc -b
$ sudo cp debian/tmp/usr/lib/xorg/modules/drivers/dummy_drv.so /usr/lib/xorg/modules/drivers/dummy_drv.so

(修正箇所)

$ diff xf86-video-dummy-0.3.3/src/dummy_driver.c mod/src/dummy_driver.c
782c782
<         pWinRoot = WindowTable[DUMMYScrn->pScreen->myNum];
---
>         pWinRoot = DUMMYScrn->pScreen->root;
Xorgの設定など

最近は xorg.conf を書かないようになってきたので、昔よりもXの設定は難しくなってきました。 xorg.conf が何か分からない人はこの方法はあきらめた方が良いと思います。分かる人が分かるように簡単に書きます。

  • xorg.conf 生成
    • Ctrl+Alt+F1 から kill でXを落とす
    • sudo X -configure で現在の設定の xorg.conf を生成する
    • できた xorg.conf をひな形として取っておく
  • xorg.conf 書き換え
    • (下の設定例を参照)
    • Monitor, Screen, Device を作る
    • xinerama を有効にする
    • ServerLayoutで相対位置設定
    • /etc/x11/xorg.conf に置く

xorg.confの内容(抜粋):

Section "ServerLayout"
	Identifier     "X.org Configured"
	Screen      0  "Screen0" 0 0
	Screen      1  "Screen1" LeftOf "Screen0" # 相対位置を指定
	InputDevice    "Mouse0" "CorePointer"
	InputDevice    "Keyboard0" "CoreKeyboard"
EndSection

Section "ServerFlags" 
    Option         "Xinerama" "1" 
EndSection

#(中略:ここに入力デバイスやScreen0関係)

Section "Device"
        Identifier "Dummy Device"
        Driver "dummy"
EndSection

Section "Monitor"
        Identifier   "monitor_dummy"
        ModelName    "dummy"
        HorizSync    10-200
        VertRefresh  20-90
        Modeline "768x1024_60.00"  65.13  768 816 896 1024  1024 1025 1028 1060  -HSync +Vsync
EndSection

Section "Screen"
        Identifier "Screen1"
        Device "Dummy Device"
        DefaultDepth 24
        SubSection "Display"
                Depth      24
#               Modes      "1024x768"       # 横置きの場合
                Modes      "768x1024_60.00" # 縦置きの場合
        EndSubSection
        Monitor      "monitor_dummy"
EndSection

設定が出来たらXを起動します。(OS再起動が楽かも)

無事起動できたら、普通にログインして以下のコマンドでディスプレイを確認します。

$ xdpyinfo -ext XINERAMA
name of display:    :0.0
version number:    11.0
vendor string:    The X.Org Foundation
vendor release number:    10900000
X.Org version: 1.9.0
 : (中略)
XINERAMA version 1.1 opcode: 147
  head #0: 1440x900 @ 768,0
  head #1: 768x1024 @ 0,0

このようにXINERAMAのセクションでheadが2つ出てくれば成功です。このとき、マウスを仮想スクリーンがある方(上の設定では左側)の画面端に持って行くと、見えない画面にマウスが移動します。

もし、Xの起動に失敗した場合(画面が真っ暗なままなど)は、コンソールやsshでログインしてXのログ(/var/log/xorg.0.log)を確認して、xorg.confの書き換えを行います。元に戻したい場合は xorg.conf を消して再起動するだけです。

VNCで接続

仮想スクリーンが準備できたらx11vncで見えない画面をVNCでアクセスできるようにします。以下のコマンドを入力します。(とりあえずパスワード無しにしていますので、実用時はパスワードを設定するか、sshトンネルなどでセキュリティを確保しておく必要があります。)

$ x11vnc -display :0 -rfbport 5901 -nopw -clip xinerama1

起動できたら、とりあえずは本体側でvncviewerで表示できるか確認しておきます。うまくいけばディスプレイの端から消えたマウスポインターは、VNC側の画面に表示されるはずです。

ここまでくれば、あとはiPadからVNCでアクセスするだけです。

完成風景

感想など

導入が大変ですが、ディスプレイ間でウインドウが移動できますので動作としては理想的です。

しかしながらXを再起動しなければならないため、iPadをセカンドディスプレイで使いたいような出先ではあんまりうれしくないです。また、XINERAMA を使うと compiz や randr が無効になってしまうのも個人的には残念です。というか、XINERAMA は開発終了なので出来れば依存したくありません。

理論上は XINERAMA 使わずにスクリーンを2つ作る方法もいけるはずなのですが、現時点の Xorg の実装では randr 対応・非対応の Device が混在するとXが落ちるようです。 Xorg 本体の randr 側が改善するか、 dummy を randr 対応するかになると思います。おそらく、 dummy 側が randr 対応するとX再起動無しでいけるようになるのではないかなと思っていますが、まだちょっとよく分かりません。誰か作ってくれるといいですね。

とにかく、 dummy ドライバは情報が少なかったので苦労しました。

参考URL:

方法2: vnc4server+x2vnc

概要

Xorg上の仕組みで頑張る方法はあきらめて、素直にVNCで拡張する方法を考えてみました。

この方法はVNCサーバーで新規にXの画面を作り、そこをVNCでiPadで表示させる方法です。ウインドウはディスプレイ間を移動できませんが、マウスとキーボードはx2vncで移動させることが出来ます。

vnc4server + x2vnc

  • 良いところ
    • X再起動必要ない
    • 導入しやすい、カスタマイズしやすい
  • 問題
    • 各ディスプレイのX環境が独立しているのでウインドウの移動が出来ない
インストール

必要なもの:

  • vnc4server
  • x2vnc

全部 synaptic, apt-get で入ります。

基本的な設定

まず、vnc4serverを起動します。初回はパスワードを設定が必要です。

$ vnc4server :1 -geometry 768x1024 -alwaysshared

ちなみに vnc4server の終了は以下のようです。動かしたサーバーは停止させるまでずっと裏で動いています。

$ vnc4server -kill :1

次に、ディスプレイの端とVNCの画面をつなげるためにx2vncを起動します。下の場合はディスプレイの左側がVNC画面と繋がります。

$ x2vnc -shared -west localhost:1

ここでもvncviewerで動作確認をしてみてください。

問題なければiPad側からVNCで接続してみます。うまく画面が表示されれば成功です。

快適化

マウスとキーボードが使えるのはいいのですが、vnc4serverがデフォルトで用意してくれる xstartup だとターミナルが一つ表示されるだけで少し使いづらいです。そこで、使いやすくカスタマイズします。

以下に自分で使っている xstartup ファイルを示します。背景を真っ白にして、VNC内でもATOKを使えるようにして、あとは狭い画面を有効に活用できるようにタイル型ウインドウマネージャーのxmonadを導入しました。

$ cat ~/.vnc/xstartup
#!/bin/sh
[ -x /etc/vnc/xstartup ] && exec /etc/vnc/xstartup
[ -r $HOME/.Xresources ] && xrdb $HOME/.Xresources
xsetroot -solid white
/opt/atokx3/bin/atokx3start.sh
vncconfig -iconic &
gnome-terminal&
exec xmonad

xmonadでは、dmenuを入れただけで特にあまりカスタマイズはしていません。

注意点

この方法ではメインのディスプレイが「:0」、VNC側が「:1」になります。基本的に各ディスプレイのX環境は独立していて、アプリケーションは起動させたディスプレイで表示され、起動後のウインドウの移動は出来ません。

Xに慣れた人なら分かると思いますが、DISPLAY変数やアプリケーションの引数などで表示するディスプレイを選ぶことが出来ます。

まれにGNOMEアプリのテーマが適用されなくなるなど、多少混乱があるようです。

感想など

個人的にはこの方法を使っています。メインの環境に全く影響が無く、セカンドディスプレイ内の通常の操作だけでなく日本語入力も問題ありません。

ウインドウの移動が出来ませんが、Twitterやログ表示のような軽いアプリであれば全く問題ありません。

方法3: x11vncの特定ウインドウ表示

概要

最後はおまけのような方法です。

x11vncで指定したウインドウをVNCでiPadに表示させるだけです。ほとんど操作は出来ませんが、iPadで表示したいウインドウが今そこにある場合には手軽で便利かも知れません。

x11vncによる特定ウインドウ表示

  • 良いところ
    • 現在表示させているウインドウをすぐに表示できる
    • 導入が一番楽
  • 問題
    • ウインドウマネージャの操作はほとんどできない
    • ウインドウの中身への操作もあまりできない
導入方法

必要なものは x11vnc のみです。synaptic, apt-get で入ります。

表示したいと思ったところで以下のコマンドを入力します。「-id pick」がポイントです。

$ x11vnc -xwarppointer -nopw -rfbport 5901 -nocursorpos -nocursor -id pick

そのあと、表示したいウインドウ内部でクリックします。

あとはiPad側からVNCで接続してみます。うまく画面が表示されれば成功です。

注意点

以下のような操作ができません。大抵 x11vnc が落ちるか、調子が悪くなります。

  • ウインドウの最小化
    • 別のウインドウを前面に出すのはOK
  • 他のワークスペースに切り替える
  • その他ウインドウの中身を非表示にする操作

また、指定したウインドウでないもの(そのウインドウから起動されたメニュー、ポップアップ、ダイアログなども)も表示できません。

上のような理由から、おそらくタイル型ウインドウマネージャーとは相性が悪いと思います。

感想

ログや Twitter の TL、R や Gnuplot のプロット画面、 LaTeX 編集時やWeb開発時の表示確認などの、ちらっと見るだけで良いものには便利です。また、ポート番号を変えれば方法1,2とも同居できます。

しかしながら、思わず最小化したりしてしまうとVNCの更新が止まってしまったりするため、個人的にはあまり使っていません。

まとめ

以上、いろいろ試してみました。方法2の vnc4server を使う方法が楽で良いと思います。

将来 xrandr 対応 dummy ドライバが出来て、自由にウインドウが移動できるようになると良いなと思っています。

他にも良い方法がありましたら是非教えてください。


各地のドキュメントやXのソースを読みながら、さらに Ubuntu 11.04 で Wayland がやってきたりするのを見て、まだまだディスプレイ関係は進化が続いて大変だなと思いました。

2011-03-04

Linux上のChromeで動くCacooエディターにスクリーンキャプチャを貼り付ける

手元のUbuntuマシンでは、Cacooの画面キャプチャ機能が使えません(SunのJava環境でも不可)。画面キャプチャは大変便利なので何とかしようと思い、Chrome拡張のChromeReplを使ってローカルの画像をCacooのFlashアプリに送り込むという回避策を考えました。

導入

手元の環境は Ubuntu 10.10 x86_64, Chrome 9.0.597.84 beta, Ruby 1.8.7 です。ChromeもRubyもapt-getで入る普通のものです。あと、画像キャプチャに ImageMagick(import, identify) も必要です。

この環境に対して ChromeRepl を入れます。以下のサイトを参考にして入れてください。これはサーバーになるChrome拡張と、それと通信するクライアントのRuby拡張から成ります。

次に、キャプチャしてCacooに転送するスクリプトを以下からダウンロードして適当なディレクトリに配置します。

使い方

  • ChromeをChromeRepl付きで起動します。
    • $ google-chrome --remote-shell-port=9222
  • Cacooにログインして新規図のエディターを起動します。
  • 「画面キャプチャ」のアイコンをクリックしてキャプチャ用のアプレットを一度起動してすぐ閉じます。
    • この操作は、エディターを立ち上げたときに1回だけ必要です。(将来無くなると便利ですね)
  • 先ほどダウンロードしたスクリプトを実行して、適当なウインドウをクリックします。
    • $ ruby chrome-cacoo-local-capture.rb
  • うまくいけばCacooのエディターにクリックしたウインドウの絵が貼り付きます。

問題なければ、上のスクリプトを適当なキーに割り当てておけば便利に使えます。

問題が起きるとすれば、何か足りないコマンドがあるとか、Chromeのポートにアクセスできないとか、そういう感じだと思います。Rubyを実行したときに出るエラーメッセージを確認してみてください。

動作解説

スクリプトは非常にシンプルなので、何をしているかはすぐに分かると思います。

  • importで画面をキャプチャしてPNGで保存
  • 画像のサイズをidentifyで取得
  • base64で画像ファイルを文字列に変換
  • ChromeReplで現在のタブのグローバル関数にアクセス
  • Flashのcapture関数に画像のデータを渡す

適当に改造することで、ローカルの画像を連続してアップロードするなどに使えるのではないかと思います。

調査

キャプチャアプリとFlashがどう通信しているかを調べた(JSのコード眺めたり、jarを逆コンパイルするなど)ところ、PNGを塩無しでBASE64テキストに変換してFlashに渡しているということが分かったので、あとはそれを実現する方法を素直に持ってきました。

JSは難読化されてない限り読むといろいろ分かっておもしろいです。また、Javaについても逆コンパイルしてコードを読むのは知っておくと役に立つことが多いと思います。(もちろんEULAの関係もありますが)

ちなみに、今回の要件はたまたま素直に出来ましたが、JavaとFlashの通信の仕方によっては難しくなっていたと思います。

ローカルとWebの接続

ChromeReplによってローカルアプリとWebアプリをつなぐことが出来ます。ブラウザの操作だけでなく、表示されているコンテンツの取得や操作が可能です。ChromeReplはChrome拡張の中でEvalしているだけのような感じなので、Chrome拡張の豊富なドキュメントを読むと良いと思います。

似たものに MozRepl などもありますので、Firefoxでも似たようなことは出来ると思います。

あと、セキュリティについては気をつける必要があると思います。細かいところをつっこむといろいろあると思いますので、今回のスクリプトではCacooで図を貼りたいときだけChromeReplを有効にするような使い方にすると話が早いと思います。

無保証

この方法の公開についてCacooの中の人には了解は取ってありますが、サポートされていない使い方ですので、動作や損害については無保証です。あしからず。

2010-12-05

Emacs上のATOKで快適日本語生活 / 2010 Emacs Advent Calendar

この記事はEmacs Advent Calender jp: 2010の5日目です。

Ubuntu上での漢字変換やEmacs上での漢字変換にはSKKを使われる方が多いと思います。導入も簡単です。ただ、自分は今まで何度も使おうと挑戦してきたのですが、挫折し続けてきました。

一方で、Ubuntuのデフォルトの変換エンジンはAnthyになっています。それなりに悪くないのですが、すぐに学習を忘れてしまうのでスーツ族には辛いです。

ということで、いろいろ試した結果、JustSystemさんの ATOK X3 にたどり着きました。とてもいいです。

普通のGUI上ではそのままで快適なのですが、Emacs上では微妙にキーバインドを取られたりして辛いです。ということで、Emacs上でATOKを快適に使う設定について書いてみようと思います。

Ubuntuユーザーで、ATOKユーザーで、しかもEmacsユーザーいう、ごく狭い対象で申し訳ありません。(例えばMacやWindows上の仮想環境でATOKのサーバーを動かして、TCP経由でホスト側からATOKを使うという状況で使えるかも知れません。。。)

あらすじ

  • インストール
  • 設定
  • キーバインド変更
  • 変換候補ポップアップ
  • 確定アンドゥ

インストール

RedHat、Debianだと簡単らしいのですが、Ubuntuは公式サポートはされていますがちょっと面倒です。amd64版だとさらに難しいです。インストールは以下のサイトの手順が参考になります。10.04と書いてありますが、10.10でもそのまま有効です。

あと、gtkの入力メソッドのリストからもれていることがあるので、一応以下のサイトを参考に、 /usr/lib/gtk-2.0/2.10.0/gtk.immodules あたりをチェックしてみてください。

お金を出して買ったものがこんなインストール方法というのもどうかと思われるかもしれませんが、裏返せばLinux版開発のリソースがあまり無いのだと思います。この値段で買えると言うだけでもすごいことだと思ってます!頑張って!

Emacsへのインストール

OSのパッケージに入っている場合がありますが、バージョンが古いとATOKと合わなかったりすることがありますので自前で入れた方が良いかもしれません。

ここからダウンロードして、以下のようにしてload-pathに置きます。

$ tar xvzf IIIMECF-0.75.tar.gz
$ cd iiimecf
$ emacs -q --no-site-file -batch -l iiimcf-comp.el
$ cp lisp/* ${どこか load-path の通ったところ}

さらに、Emacs上でGUI経由での漢字変換を無効にするために、emacsの起動方法を変えます。手元では、emacsは /usr/local/emacs-trunk/ に自前コンパイルで入っていますので、 /usr/local/bin/emacs を以下のようなシェルスクリプトにして起動しています。

XMODIFIERS=@im=none exec /usr/local/emacs-trunk/bin/emacs

基本設定

次にEmacsの設定ですが、まず自分の所の基本設定を示してみます。キーバインドが鬼門です。

;; ATOK X3 on IIIMECF

;; 基本設定
(setq iiimcf-server-control-hostlist 
      (list (concat "/tmp/.iiim-" (user-login-name) "/:0.0")))
(require 'iiimcf-sc)
(setq iiimcf-server-control-default-language "ja")
(setq iiimcf-server-control-default-input-method "atokx3")
(setq default-input-method 'iiim-server-control)

;; キーバインド
(setq iiimcf-keycode-spec-alist
      (append 
       '((13 10 0)       ; c-m commit -> [Enter]

         (7 27)          ; c-g -> [esc]
                         ;; MS-IMEのキー体系を仮定
         (1 36 65535)    ; c-a head position -> [Home]
         (5 35 65535)    ; c-e last position -> [End]
         (2 37 65535)    ; c-b backward bunsetu -> [←]
         (16 38 65535)   ; c-p previous candidate -> [↑]
         (6 39 65535)    ; c-f next bunsetu -> [→]
         (14 40 65535)   ; c-n next candidate -> [↓]
         
         (11 37 65535 1) ; c-k narrow bunsetu -> [Shift+←]
         (12 39 65535 1) ; c-l widen bunsetu -> [Shift+→]
         
         (21 121 65535)  ; c-u alphabet -> [F10]
         (15 118 65535)  ; c-o kana -> [F7]
         )
       iiimcf-keycode-spec-alist))
;; ↑変換リストの意味
;; [emacs event] : Emacs上のキーコード(キャラクタコード)
;; [translate keycode] : serverに送りたいキーコード (Javaのajava.awt.evet.KeyEventの値を参照)
;; [translate keychar] : serverに送りたいキーの文字。65535だと何も送らない。
;; [modifier] : 1:shift, 2:ctrl

;; 半角スペース
(defun atok-insert-half-space ()
  (interactive) (insert " "))
(define-key iiimcf-server-control-initial-state-keymap
  (kbd "S-SPC") 'atok-insert-half-space)

最初の設定が必須項目です。

次の数字が並んでいるところがキーバインド設定です。ここは後述します。

最後はShift+Spaceで半角スペースを入れるためのキーバインド追加です。

キーバインド変更について

まずIIIMFのキー入力の考え方を書きます。

IIIMFでは基本的にサーバー側(変換エンジン側)でほとんどの処理を行います。つまり、処理のキーバインドの内容を決めるのはサーバー側で、クライアント側(Emacs側)はサーバーが送ってきた表示指示に従って描画しているだけです。基本的にはATOKのキー設定がそのまま全クライアントで共通に使えるようになります。

IIIMFの動作イメージ

しかしながら、EmacsのキーイベントとATOK(IIIMP)とのキーイベントにミスマッチがあるので、そこを埋めるために変換テーブルで変換しています。それが上の設定にある iiimcf-keycode-spec-alist です。デフォルトの設定はある程度の変換が書かれているのですが、Ctrl系のキーが効かないようです(原因はまだ追えてません)。

そこで、Emacs上で行うCtrl系のキー入力を、目的の機能を持つ別のキーに変換するようにテーブルを書きます。上の例では以下のように変換しています。

;;           Emacs上のキー、機能  ->  ATOKのキー
'((13 10 0)       ; c-m 確定 -> [Enter]
  (7 27)          ; c-g キャンセル -> [esc]

  (1 36 65535)    ; c-a 先頭移動 -> [Home]
  (5 35 65535)    ; c-e 末尾移動 -> [End]
  (2 37 65535)    ; c-b 文節左移動 -> [←]
  (16 38 65535)   ; c-p 前候補 -> [↑]
  (6 39 65535)    ; c-f 文節右移動 -> [→]
  (14 40 65535)   ; c-n 次候補 -> [↓]
  
  (11 37 65535 1) ; c-k 文節縮める -> [Shift+←]
  (12 39 65535 1) ; c-l 文節伸ばす -> [Shift+→]
  
  (21 121 65535)  ; c-u アルファベット化 -> [F10]
  (15 118 65535)  ; c-o カタカナ化 -> [F7]

ATOK側でCtrl系でないキーに必要な機能が割り振ってある必要があります。また、上の例ではMS-IME的な操作になってますので、他のキーが好きな人は適当に変える必要があります。

ATOK側に送るキーコードはJavaのキーコードを使います。定数の値については JDK のドキュメント(例えばここ)を参照してください。

全角・半角スペース

次に、全角と半角のスペースの使い分けもちょっとうまくいかないので、ちょっと手を入れます。普通にスペースを押したときにどちらを入力するかはATOKの設定で行うのですが、Emacs経由だとうまく使い分けられません。そこで、iiimcfのキーマップで制御します。

iiimcfのキーマップには主に2つあります。入力待ち中のキーマップ iiimcf-server-control-initial-state-keymap と、入力中のキーマップ iiimcf-server-control-preedit-state-keymap です。これらに定義してあるキー入力はサーバー側に送られずにクライアント側のみで処理されます。

ということでスペースの使い分けはここで入れることにします。上の最後の部分がその設定です。

以上で、大抵の設定についてはカバーできるかなと思います。

変換候補のポップアップ

IIIMECF 0.75 では、変換候補の一覧がミニバッファに表示されます。

以前も書きましたが、画面が広くなった現在においてはミニバッファは遠すぎます。

そこで id:m2ym さんの popup.el を使って変換文字列の下に表示するようにしてみました。以下のコードで既存の処理を乗っ取ってポップアップさせます。

変換候補のポップアップ

;; IIIMECFで変換候補一覧ポップアップ
(require 'popup)

(defvar iiimcf-UI-draw-lookup-choice-popup-instance nil)

(defun iiimcf-UI-draw-lookup-choice-popup-position ()
  ;; uic via dynamic scope!!!
  (let* ((pos (marker-position (iiimcf-UI-marker uic)))
         (text (iiimcf-UI-preedit-text uic))
         (len (length text))
         (pts 0) pte cprop position)
    (setq text (copy-sequence text))
    (while pts
      (setq cprop (get-text-property pts 'iiim-feedback text)
            pte (next-single-property-change pts 'iiim-feedback text))
      (if (eq 'reverse (car cprop))
          (setq position (+ pos pts) pts nil)
        (setq pts pte)))
    position))

(defun iiimcf-UI-draw-lookup-choice-popup (mk candidates)
   (let ((cands (aref candidates 0))
         (index (aref candidates 1))
         (title (aref candidates 2)) lst)
     (setq lst (loop for pair in cands
                     for num = (car pair)
                     for str = (cdr pair)
                     collect (popup-make-item 
                              (format "%s:%s" num str))))
     (unless (and 
              iiimcf-UI-draw-lookup-choice-popup-instance
              (popup-p iiimcf-UI-draw-lookup-choice-popup-instance))
       (setq iiimcf-UI-draw-lookup-choice-popup-instance
             (popup-create (iiimcf-UI-draw-lookup-choice-popup-position) ; pos
                           (popup-preferred-width lst)  ; width
                           10 ; height
                           :around t
                           :face 'popup-menu-face
                           :selection-face 'popup-menu-selection-face)))
     (popup-set-list iiimcf-UI-draw-lookup-choice-popup-instance lst)
     (popup-draw iiimcf-UI-draw-lookup-choice-popup-instance)
     (popup-select iiimcf-UI-draw-lookup-choice-popup-instance index)))

(defun iiimcf-UI-clear-lookup-choice-popup (marker)
  (when 
      (and 
       iiimcf-UI-draw-lookup-choice-popup-instance
       (popup-p iiimcf-UI-draw-lookup-choice-popup-instance))
    (popup-delete iiimcf-UI-draw-lookup-choice-popup-instance))
  (setq iiimcf-UI-draw-lookup-choice-popup-instance nil))

(defadvice iiimcf-UI-draw-lookup-choice-echo-line
  (around iiimcf-ui-popup (mk candidates))
  (iiimcf-UI-draw-lookup-choice-popup mk candidates)
  ;ad-do-it
  )

(defadvice iiimcf-UI-clear-lookup-choice-echo-line
  (around iiimcf-ui-popup (mk))
  (iiimcf-UI-clear-lookup-choice-popup mk)
  ;ad-do-it
  )

(ad-activate-regexp "^iiimcf-ui-popup")

ポップアップ表示位置を計算するために呼び出し元のローカル変数に入っている uic という変数が使いたかったのですが、引数で取ってくるのが大変そうだったのでダイナミックスコープで取ってきました。

ダイナミックスコープは id:podhmo さんの fletで関数を横取りする でも書かれています。ダイナミックスコープは、こんな風にごにょごにょする際にちょっと便利です。便利ではありますが明らかに正しくない使い方ですので、プロトタイプやちょっとした拡張にとどめておいた方が良いと思います。

手元では大体うまく動いていますが、もしかしたら微妙な挙動があるかも知れません。

確定アンドゥ

最近の漢字変換では確定アンドゥの機能が付いています。 IIIMECF 0.75 ではそのままでは確定アンドゥが使えませんが、確定アンドゥのキーイベントをATOKサーバー側に送れば実現できました。

以下コードと設定例です。

(defun atok-undo-commit ()
  (interactive)
  (when iiimcf-server-control-ic-id
    (iiimcf-server-control-keyforward last-input-event)
    (iiimcf-set-icfocus iiimcf-server-control-ic-id)))

(define-key iiimcf-server-control-initial-state-keymap
  (kbd "<C-backspace>") ; ATOKでは C-BS に確定アンドゥが割り当ててある
  'atok-undo-commit)

その他の機能(辞書登録とか)も頑張れば使えるのかもしれませんが、ちょっと試したぐらいでは実現できませんでした。

まとめ

ということで、UbuntuEmacs上でのATOKについて書いてみました。これまでのコードをgistにまとめました。

変換候補のポップアップと確定アンドゥは既に手に馴染んでいます。全く違和感がありません。つまり、使えて当たり前でいままで出来無かったことの方が異常だったのだと思います。これで普通になれました。

ちなみに、上で書いたコードは少ないのですが、このためには IIIMECF のコード(elisp)と IIIMF の white paper、プロトコル仕様書(i18n.orgはリンク切れですが、ATOKのCDに入っています)、後は popup.el のコード(elisp)、Emacsのイベントに関する文書(info)などを読みました。全部で1週間以上かかっています。大体10年ほど前の設計や実装ですが、綺麗なコードと設計で大変勉強になりました。ただ、IIIMFの仕様はやっぱり「Sunが作った」という感じかなと思いました。


仕様を調べているうちに、設計者の樋浦さんが亡くなられていたということを知りました。IIIMFや国際化に尽力された樋浦さんのご冥福をお祈りいたします。