1. 概要

"tanasinn terminal emulator" は Firefox/Thunderbird/SeaMonkey上で動作する端末エミュレータです。
Windows(XP/Vista/7) + Cygwin / Mac / Linux / FreeBSD 環境で動作します。
ほぼJavascriptによる実装(2%のPython)です。
コマンドラインシェルやターミナルエディタが提供するCUI/TUIを用いて、オペレーティングシステムの
機能群にブラウザからアクセスすることができます。

2. News

2.1. 2012/05/05

Version 0.2.1.1
Supports inline Sixel Graphics.
Supports UTF8/urxvt/SGR style mouse reporting.
Improve performance.

2.2. 2012/04/18

Version 0.2.0.0
More precise emulation.
Now it passes vttest 1, 2, 3, 4, 7, and 8.
Improve performance.

2.3. 2012/04/15

Version 0.1.7.2
Important bug fix, Now it works en_US locale, sorry!

2.4. 2012/04/08

Now version 0.1.7.1 available.
  • Implement SGR blink feature.

  • Implement SGR rapid blink feature(from DOS’s ANSI.SYS).

  • Implement SGR italic feature.

  • Implement SGR halfbright feature.

    重要:0.1.5.0 / 0.1.5.1はビルド時のバグのため「動きません」。
            0.1.5.2は一部のプラットフォームで動きません。
            いろいろ試行錯誤しちゃった人はすみません。
    Python2.x系のパスやCygwinのルートディレクトリなどを探しまわり、環境情報を推定しようとします。
    動かなかった場合は %USERPROFILE%¥.tanasinn.js (Windows)、または~/.tanasinn.js(それ以外のOS) に、
    このように書いてみてください。
%USERPROFILE%¥.tanasinn.js or $HOME/.tanasinn.js
process.cygwin_root = "C:\\cygwin";
process.bin_path = "/bin:/usr/bin:/usr/local/bin:/opt/local/bin";
process.python_path = "/usr/bin/python"
それでもだめな環境もあるかもしれません。
こちらからはtwitter用ハッシュタグ #tanasinn / #tanasinnが動かない あたりで探してみます。
拡張フォルダ内のディレクトリ名が歴史的理由により意味不明になっていたので、かなり変えました。

3. ダウンロード/ライセンス

Mozilla Public License 1.1 (MPL1.1) で配布しています。

4. 特色

  • Geckoベースのユーザーインターフェース (HTML Canvas + XUL)。柔軟でカスタマイズが容易です。

  • マルチプラットフォーム。現在サポートされるのは、Mac / FreeBSD / Linux(というかUbuntu) / Windows(XP/Vista/7) + Cygwin環境 (POSIX版Pythonが必要)

  • プラグインアーキテクチャ。プラグアンドプレイでのインストール・アンインストールが可能。

  • ブラウザを再起動したり閉じたりしている間、ターミナルセッションを保持します。 サスペンドしたセッションに後から再接続できます。

  • デバッガ内臓。ステップ実行やトレースができます。 ターミナルとアプリケーションがやり取りするI/Oシーケンスをリアルタイムで観察できます。

  • スクロールバック、代替スクリーン、ビジュアルベル、オーディオベルなど基本的な機能はサポートしています。

  • IMEが使えます。

  • VT52互換モード搭載。

  • Tektronix 4010/4014互換モード搭載 (一部未実装)。

  • Sixelグラフィクスモードをサポート。

  • PC端末では実装例の少ないロスト・コマンドを積極的に実装。 DECDHL/DECDWLやDRCS(Dynamically Redefined Character Set)など。

  • East Asian Width(ひらがななどの文字幅を考慮したレンダリング)に一応対応しています。 B集合以降のCJK統合拡張漢字等、サロゲートペアで表現される文字にも対応しています。 異体字セレクタ、インド系諸語等で頻出する合字やアラビア系文字の置換などの複雑なグリフ処理は、 現在主にパフォーマンス上の理由により実装されませんが、 別途プラグインで対応することも可能です。 bidiには完全に対応できていないと思います。

  • 地域化のサポート。gettextのようなスタイルを採用しています。 ローカライズリソースをUI上から編集することができます。

  • Geckoがサポートする様々なエンコーディングを指定できます。

  • ウィンドウの透明化をサポート。これはブラウジングしながらターミナルで作業したい時に便利です。

  • マウスエミュレーションをサポート。xtermスタイルのノーマルレポーティング、ホイールスクロール、 UTF8/urxvt/SGRスタイルの拡張レポーティングにも対応。

  • テキストの選択や矩形選択ができます。

  • ドラッグアンドドロップやキーボードショートカットによる選択テキストのコピー・ペーストが可能です。

  • ドラッグによる移動・リサイズができます。

  • 256色表示対応。32bitカラーをパレットに割り当てることができます。

  • VimperatorやKeysnailのようなコマンドラインインターフェース。

  • Vimperator/Muttator のプラグインとして導入することができます。必要なら"<C-i>"や"gF"マッピングをフックすることができます。

  • 一部のプラットフォームでトラックパッドジェスチャー対応。 ピンチオープン・クローズ、スワイプ、回転を別のキーイベントにマップすることが可能。

  • スタンドアロンのXULアプリケーションとして動作させることも頑張れば可能。

5. スクリーンショット

images/nyan.png
Figure 1. "Nyan Cat"を走らせたところ。
images/bb2.png
Figure 2. "BB"(アスキーアートのデモプログラム)を走らせたところ。
images/color2.png
Figure 3. colortable.sh and 256color2.plでカラーチャートを表示させたところ。
images/ime2.png
Figure 4. IMEで日本語を打ってみたところ (AquaSKK使用)。
images/scroll2.png
Figure 5. スクロールバーインターフェースは必要なときにあらわれます(OSX Lionのように)。
images/overlay2.png
Figure 6. オーバーレイインジケータがフォントサイズ、ウィンドウサイズ、タイトルバーメッセージが変更されたときなどに表示されます。
images/completion2.png
Figure 7. VimperatorKeysnail にインスパイアされたコマンドラインインターフェース。
images/fontfamily2.png
Figure 8. フォントファミリを選択しています。
images/fontsize2.png
Figure 9. フォントサイズを選択しています。
images/launcher2.png
Figure 10. QuckSilverUbiquity にインスパイアされたランチャーインターフェース。
images/session2.png
Figure 11. デタッチ済みのセッションに再接続しているところ。ブラウザがクラッシュしたり再起動しても安心です。
images/sl2.png
Figure 12. "sl" (ジョークウェア)を走らせたところ。
images/debug2.png
Figure 13. セルフデバッギング。ターミナルI/Oシーケンスをリアルタイムにトレースしています。
images/vim.png
Figure 14. ポップアップメニューを表示しています。Vimの方をちょっと改造しました。
images/w3m2.png
Figure 15. ブラウザの中でブラウザ。プライベートパッチを当てたw3mでインラインイメージを表示させました。ここまでくるとちょっと意味不明ですね。
images/wl3.png
Figure 16. メーラーの中でメーラー。Wonderlustをemacs上で動かしているものをThunderbird上で動かしています。
images/xulapp.png
Figure 17. tanasinnをtanasinnでメタ開発中。これはスタンドアロンXULアプリケーションとして動作させているところです。
images/roflbalt.png
Figure 18. roflbalt。Rubyで書かれたゲームです。
images/zsh.png
Figure 19. zshのcorrect機能にプライベートシーケンスを仕込みました。シーケンスハンドラの応用例です。
images/tektronix.png
Figure 20. Tektronix 4010/4014 エミュレーション。
images/sixel.png
Figure 21. Sixel Graphics エミュレーション。
images/decdhl.png
Figure 22. DECDHL/DECDWL。
images/drcs.png
Figure 23. DECDHLおよびDECDLD/DRCS(Dynamically Redefined Character Set)のあわせ技でGuiness。
images/nethack.png
Figure 24. jnethackで遊んでいるところ。
images/shellshock.png
Figure 25. ShellShockで遊んでいるところ。
images/mplayer.png
Figure 26. mplayerで動画を再生したところ。

6. 将来的にサポートされる仕様

  • エミュレーションの強化。今はとりあえずxterm系コンパチブルなエミュレーションで作ってますが、 本来はterminfoエントリを見るべきでしょう。

  • カラースキーマのサポート。16色or256色のパレットをインポート/エクスポートできるように。

  • スキンのサポート。痛端末化が可能に。

  • cursesベースのアプリーションとの連携強化。 端末アプリケーションからGeckoにアクセスしてポップアップとか動画表示とかOAuthとかやるための仕様の策定。 特別なプラグインを書かずとも端末からyoutubeを見たりtwitterやったりできるようになります。

  • プラグイン開発用ドキュメント。

7. 動作要件

  • Firefox 4.02pre+ / Thunderbird 7.0a1+ / SeaMonkey 2.1+.

  • POSIX用にビルドされたPython (2.5+, Mac、FreeBSD、Linux、 または cygwin用のもの)。 Python for Win32 や ActivePython 等はforkや仮想端末をサポートしないと思うのでダメしょう。

  • POSIX環境。 もしWindowsをお使いでしたら、C:\cygwinにCygwinをインストールしてください。

8. インストール

8.1. Firefox/Thunderbird/SeaMonkey拡張としてビルドする:

Mac, FreeBSD, Linux, Cygwin
git clone git://zuse.jp/git/tanasinn.git
cd tanasinn
./configure
make
ビルドが成功すると、tanasinn@zuse.jpというXPIファイルが生成されます。
これをインストールしてください。

8.2. Vimperator/Muttatorプラグインとしてインストール:

Vimperator/Muttatorのランタイムディレクトリ以下にリンクを作成します。
Mac, FreeBSD, Linux
git clone git://zuse.jp/git/tanasinn.git
cd tanasinn
ln -s $PWD $RUNTIME/tanasinn
ln $PWD/modules/tanasinn-vimp.js $RUNTIME/plugin/
Windows Vista/7 + Cygwin
git clone git://zuse.jp/git/tanasinn.git
cd tanasinn
$(cygpath $COMSPEC) /c mklink /D "$(cygpath -w "$RUNTIME/tanasinn")" "$(cygpath -w "$PWD")";
ln $PWD/modules/tanasinn-vimp.js $RUNTIME/plugin/
Windows XP + Cygwin

Windowsネイティブなシンボリックリンクを作成するために、Windows Server 2003 Resource Kit Tools をインストールする必要があるかもしれません。

git clone git://zuse.jp/git/tanasinn.git
cd tanasinn
$(cygpath $COMSPEC) /c linkd.exe "$(cygpath -w "$RUNTIME/tanasinn")" "$(cygpath -w "$PWD")";
ln $PWD/modules/tanasinn-vimp.js $RUNTIME/plugin/
$RUNTIMEは'$HOME/.{vimperator,muttator}'のようなVimperator/Muttatorのランタイムディレクトリです。

8.3. KeySnail経由でインストール:

keysnail.js
............

void function install_tanasinn() {

    var file = "file:///…………/tanasinn/modules/common/process.js"; // <-- process.jsのパス(URL表現)

    var contractID = "@zuse.jp/tanasinn/process;1";
    var process_class = Components.classes[contractID]
    if (!process_class) {
        Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
            .getService(Components.interfaces.mozIJSSubScriptLoader)
            .loadSubScript(file);
        process_class = Components.classes[contractID]
    }
    var process = process_class
        .getService(Components.interfaces.nsISupports)
        .wrappedJSObject;

    shell.add(["tanasinnlaunch","tla[unch]"],
              "Show tanasinn's Launcher.",
              function () {
                  process.getDesktopFromWindow(window)
                      .post("command/show-launcher");
              }, {
                argcount: "0",
                bang: true
              });

    shell.add(["tanasinnstart","tst[art]"],
              "Run a command on tanasinn.",
              function (args, extra) {
                  process.getDesktopFromWindow(window)
                      .post("command/start-session", extra.left);
              }, {
                argcount: "?",
                bang: true
              });
} ();

............

9. 使いかた

9.1. 端末の起動

images/launcher.png

9.1.1. 拡張としてインストールした場合

Controlキーの2度押し (ctrl + ctrl) でコマンドラインランチャーインターフェースが出現します。
そこで例えば、"man man[Enterキー]" とタイプしてみてください。
ウィンドウのようなUIがブラウザウィンドウ内に現れ、manページが表示されると思います。

9.1.2. Vimperator/Muttatorのプラグインとしてインストールした場合:

アドオン版と同様、Controlキーの2度押し (ctrl + ctrl) でtanasinnが起動します。
Vimperator/Muttatorのコマンドライン上から起動したい場合は、以下のようにタイプしてください。
:tanasinnlaunch
もしターミナルスクリーンをVimperator/Muttatorから直接起動させたければ、以下のようにします:
:tanasinnstart <コマンド>

example:

:tanasinnstart emacs -nw
tanasinnにコマンドを送信したいときは、以下のようにします:
:tanasinncommand <コマンド>

example:

:tanasinncommand overlayecho Hello, World!
tanasinnにキー入力を送信したいときは、以下のようにします:
:tanasinnsendkeys <キーシーケンス式>

example:

:tanasinnsendkeys <nmode><C-z>top<CR>

9.2. 基本的な操作方法

xtermのような端末エミュレータを使ったことがあれば大体いけると思いますが、
以下のような特筆すべき仕様もいくつかあります。
shift + shift (shiftキーの2度押し)
ターミナルとコマンドラインテキストボックスの間でフォーカスを切り替えます。
shift + マウスドラッグ
ウィンドウの移動。
alt + マウスで範囲選択
矩形選択。 (Terminal.appの真似)
ピンチオープン/クローズ
フォントサイズの拡大・縮小。 (iTerm2の真似)

9.3. コマンドラインインターフェースを使う

images/commandline.png
[bold]*shift+shiftでコマンドラインにカーソルが移動します*。
これだけおぼえておけばとりあえず補完でなんとかなると思います。

9.3.1. 文字コード設定

decoder

アプリケーションの出力を解釈するための文字コード体系を設定します。

decoder [キャラクタセット・符号化方式名]
encoder

アプリケーションへの入力操作を行うときの文字コード体系を設定します。

encoder [キャラクタセット・符号化方式名]

使用可能な文字コード名はGeckoに依存します。補完で確認してください。

images/decoder.png

9.3.2. 地域化

localize

メッセージやUI要素のローカライズを行います。

localize [ロケールID] "[メッセージID]" "[翻訳済み文字列]"

[ロケールID](LCID)は日本語環境をお使いならたいていja-JPだと思います。

images/localize.png

[メッセージID]はgettextのように英語表現になっています。 この設定は通常の永続化機構とは別の仕組みで保存されますので、 saveprofileのような保存操作はとくに必要ありません。

9.3.3. 外観の設定

fgcolor

前景色を設定します。

fgcolor [色番号] [CSS color]

[色番号]に対応する前景色パレットに、[CSS color]で表現される色が設定・適用されます。

bgcolor

背景色を設定します。

bgcolor [色番号] [CSS color]

[色番号]に対応する背景色パレットに、[CSS color]で表現される色が設定・適用されます。

images/bgcolor.png

色番号は0から255までの整数です。 通常の16色カラー端末では0から15までが使用されます。

fontfamily

フォントファミリを設定します。

fontfamily [フォント名]

ターミナルのフォントファミリが[フォント名]に設定・適用されます。

images/fontfamily3.png
fontsize

フォントサイズを設定します。

fontsize [フォントサイズ]

ターミナルのフォントサイズが[フォントサイズ]pxに設定・適用されます。 単位がpixelであることに注意してください。

images/fontsize3.png
increase

フォントサイズを1px増加させます。

increase

数字のプレフィクスをつけて、以下のようにすると、フォントサイズが5px増加します。

5increase
decrease

フォントサイズを1px減少させます。

decrease

数字のプレフィクスをつけて、以下のようにすると、フォントサイズが5px減少します。

5decrease

9.3.4. ウィンドウ操作

up / down / left / right

ウィンドウを上下左右に移動させます。移動量はデフォルトで60pxです。 この値は、設定値moveshortcut.stepで変更できます。

数字のプレフィクスをつけて、たとえば以下のようにすると、5 x 60px = 300px 右に移動します。

5right
wider / narrower / shorter / taller

ウィンドウサイズを調整します。

数字のプレフィクスをつけて、たとえば以下のようにすると、スクリーンが5カラム拡大します。

5wider

9.3.5. blur (new!)

ウィンドウからフォーカスをはずします。

blur

9.3.6. 編集系操作

copy

選択されたテキストをクリップボードへコピーします。

copy
paste

クリップボード内のテキストを現在のエンコーダーで符号化し、端末に送信します。

paste
順次解説予定

9.3.7. マッピング管理

9.3.8. nmap

9.3.9. nnoremap

9.3.10. nunmap

9.3.11. cmap (new!)

9.3.12. cnoremap (new!)

9.3.13. cunmap (new!)

9.3.14. コンポーネント管理

lscomponent
disable

プラグインを無効化します。

disable [プラグインID]
enable

プラグインを有効化します。

enable [プラグインID]

9.3.15. 履歴管理

clearhistory

コマンドラインヒストリをクリアします。

clearhistory

9.3.16. セッション操作

kill/quit

現在のセッションを終了し、ウィンドウを閉じます。

kill

または

quit
detach

現在のセッションをサスペンドし、ウィンドウを閉じます。

detach

9.3.17. プロファイル操作

loadprofile

永続化されたセッションプロファイルをロードします。 以下のようにすると"default"という名前のセッションプロファイルが "$HOME/.tanasinn/persist/default.js" から読み込まれます。

loadprofile

もし別のプロファイルを読み込みたければ:

saveprofile [プロファイル名]

としてください。 [プロファイル名]は英数字、ドット(.)、アンダースコア(_)、ハイフン(-)が使用可能です。

saveprofile

現在のセッション設定を永続化します。 以下のようにすると現在のセッションプロファイルが"default"という名前で "$HOME/.tanasinn/persist/default.js" へ書き出されます。

saveprofile

もし別の名前で書き出したければ:

saveprofile [プロファイル名]

としてください。 [プロファイル名]は英数字、ドット(.)、アンダースコア(_)、ハイフン(-)が使用可能です。

deleteprofile

永続化されたセッション設定を削除します。 以下のようにするとセッションプロファイル"default"が削除されます。

saveprofile

もし別のセッションプロファイルを削除したければ:

deleteprofile [プロファイル名]

としてください。 [プロファイル名]は英数字、ドット(.)、アンダースコア(_)、ハイフン(-)が使用可能です。

globalload
globalsave
globaldelete

9.3.18. パネル操作

openpanel

パネルを表示します。

openpanel
closepanel

パネルを隠します。

closepanel

9.3.19. デバッグ支援

console

コンソールパネルの表示状態をトグルします。

console
debugger

デバッガパネルの表示状態をトグルします。

debugger

9.3.20. 低レベル設定操作

set
setglobal

9.3.21. 低レベルメッセージング操作

publish

tupstartメッセージを発行します。

publish <メッセージ式> <Javascriptコード>

example

publish command/report-overlay-message "Hello, World!"

9.3.22. ユーティリティ

sendkeys (new!)

指定されたキーシーケンス式を解釈し、キーイベントとして実行します。

sendkeys [キーシーケンス式]

example:

sendkeys <nmode><C-z>echo Hello, World!<CR>fg<CR>
sendkeys <PinchOpen>
source (new!)

指定されたファイルをtanasinnコマンドバッチファイルとして読み込み、実行します。

source [ファイル・パス]
import (new!)

指定されたファイル名を~/.tanasinn/batchesディレクトリから検索し、コマンドバッチファイルとして実行します。

import [ファイル名]
execcgi (new!)

指定されたファイル名を~/.tanasinn/cgi-binディレクトリから検索し、実行した結果をコマンドバッチとして再解釈し、実行します。

execcgi [ファイル名]

引数で指定されるファイルは実行可能なものであればなんでもよいです。 たとえば、

  #!/usr/bin/env perl

  print <<EOM
  overlayecho Hello, World!
  EOM

のようにすればperlで記述できますし、

  #!/usr/bin/emacs23 -nw --script

  (princ "enable console")

のようにすればelispが使えます。

vimperator (new!)

vimperatorにコマンドを送ります。

vimperator [command]
echo (new!)

ステータスラインにメッセージを表示します。

echo [メッセージ]
overlayecho (new!)

オーバーレイインジケータにメッセージを表示します。

overlayecho [メッセージ]
screenshot (new!)

現在のスクリーンを画像形式(png)で保存します。

screenshot [ファイル名]

たとえば

screenshot abc

とすると、{$HOME,%USERPROFILE%}/.tanasinn/screenshot/abc.pngにスクリーンショット画像が保存されます。

javascript

9.4. ユーザーディレクトリ

~/ or %USERPROFILE%/
   |
   +-- .tanasinn.js    <-------------------------------- (1)
   |
   +-- .tanasinn/      <-------------------------------- (2)
         |
         +-- tanasinnrc          <---------------------- (3)
         |
         +-- sessions.txt        <---------------------- (4)
         |
         +-- desktop_profile/    <---------------------- (5)
         |
         +-- session_profile/    <---------------------- (6)
         |
         +-- persist/            <---------------------- (7)
         |
         +-- history/            <---------------------- (8)
         |
         +-- batches/            <---------------------- (9)
         |
         +-- cgi-bin/            <---------------------- (10)
         |
         +-- screenshot/            <------------------- (11)
         |
         +-- log/                <---------------------- (12)
         |
         +-- modules/            <---------------------- (13)
                |
                +-- process_components/          <------ (14)
                |
                +-- desktop_components/          <------ (15)
                |
                +-- session_components/          <------ (16)
                |
                +-- shared_components/           <------ (17)

9.4.1. .tanasinn.js

起動時(Processオブジェクトが生成されるタイミング)に読み込まれ、実行されるJavascriptファイル。

~/.tanasinn.js Or %USERPROFILE%¥.tanasinn.js
process.cygwin_root = "C:\\cygwin";
process.bin_path = "/bin:/usr/bin:/usr/local/bin:/opt/local/bin";
process.python_path = "/usr/bin/python"

9.4.2. .tanasinn/

ランタイムディレクトリ。

9.4.3. .tanasinn/tanasinnrc

セッション生成時(Sessionオブジェクトが生成されるタイミング)に読み込まれ、1行づつ実行されるtanasinnコマンドファイル。

設定例:

.tanasinn/tanasinnrc
nmap <C-S-o> <cmode>detach<CR>
nmap <C-S-i> <2-shift>blur<CR>
nnoremap <C-S-z> <2-shift>
cmap <C-z> <2-shift>
cmap <C-s> <C-a><C-k>vimperator set!<CR>
set renderer.normal_alpha=0.8
set renderer.font_size=20
set outerchrome.background_opacity=0.8
set outerchrome.background="-moz-radial-gradient(center, circle closest-side, #404040, #000)"

9.4.4. .tanasinn/sessions.txt

デタッチ中のセッションを管理するためのファイル。

9.4.5. .tanasinn/desktop_profile/

Desktopオブジェクトの設定が書き出されるディレクトリ。

9.4.6. .tanasinn/session_profile/

Sessionオブジェクトの設定が書き出されるディレクトリ。

9.4.7. .tanasinn/persist/

休眠中のセッションが永続化されて書き出されるディレクトリ。

9.4.8. .tanasinn/screenshot/

スクリーンショット画像が書き出されるディレクトリ。

9.4.9. .tanasinn/log/

ログが書き出されるディレクトリ。

9.4.10. .tanasinn/batches/

importコマンドの引数として与えられたファイル名が検索されるディレクトリ。

9.4.11. .tanasinn/cgi-bin/

execcgiコマンドの引数として与えられたファイル名が検索されるディレクトリ。

9.4.12. .tanasinn/modules/

このディレクトリ内にユーザー拡張やユーザーデータが配置されます。

9.4.13. .tanasinn/modules/process_components/

プロセスバスに接続されるユーザー拡張が配置されるディレクトリ。

9.4.14. .tanasinn/modules/desktop_components/

デスクトップバスに接続されるユーザー拡張が配置されるディレクトリ。

9.4.15. .tanasinn/modules/session_components/

セッションバスに接続されるユーザー拡張が配置されるディレクトリ。

9.4.16. .tanasinn/modules/shared_components/

デスクトップバス・セッションバス両方に接続されるユーザー拡張が配置されるディレクトリ。

10. Tips

10.1. モジュールの有効・無効を切り替えるには

コマンドラインから以下のようにタイプしてみてください。

enable <プラグインID>
disable <プラグインID>

10.2. セッションの設定を永続化・ロードするには

コマンドラインから以下のようにタイプしてみてください。

saveprofile

こうすれば現在のセッションの設定が "$HOME/.tanasinn/persist/default.js" へ書き出されるでしょう。 もし別の名前で書き出したければ:

saveprofile <プロファイル名>

これで設定が"$HOME/.tanasinn/profile/<プロファイル名>.js"に書き出されます。

この設定を再び読み込むには以下のようにします:

loadprofile <プロファイル名>

10.3. 現在のセッションからデタッチするには

コマンドラインから以下のようにタイプしてみてください。

detach

セッションはサスペンドされます。ランチャーインターフェースからこのセッションに再接続することができます。

10.4. 現在のセッションを終了するには

コマンドラインから以下のようにタイプしてみてください。

kill

10.5. Vimperator/Muttatorプラグインとしてインストールしたとき、 "<C-i>"(インプットフィールドを外部エディタで編集) と "gF"(HTMLソースを外部ビューワで閲覧) マッピングをフックするには

インプトッフィールドをtanasinn上の外部エディタで編集したければ、vimperatorrc/muttatorrcに 以下のコマンドを追加してください。

let g:tanasinneditorcommand="vim %"

この例ではvimコマンドがシステムにインストールされている必要があります。 「%」の部分はテンポラリファイルのパスに置換されます。

HTMLソースをtanasinnで見るには、以下のコマンドを追加します。

let g:tanasinnviewsourcecommand="vim +e '%'"

「%」はエスケープ済みのURL文字列に置換されます。 この例ではvimのインストールが必要になります。

10.6. Vimで現在のウィンドウにブラウザをオーバーレイで出したい

「Overlay Browser」という拡張をやっつけで書いてスナップショットビルドに入れておいたので、とりいそぎですが使用方法をご説明します。

以下のようなVim Scriptを書いてください。

function! s:open_gecko_window(src)
    let nr = winnr()
    let col = wincol()
    let line = winline()
    let width = winwidth(nr)
    let height = winheight(nr)
    call system('echo -en "\x1b]210;'.col.' '.line.' '.width.' '.height.' '.a:src.'\x07" > /dev/tty')
endfunction

function! s:close_gecko_window()
    call system('echo -en "\x1b]211;\x07" > /dev/tty')
endfunction

command -nargs=1 OpenMozilla call s:open_gecko_window(<f-args>)
command CloseMozilla call s:close_gecko_window()

このようにして使用します。

OpenMozilla http://google.com
images/browser.png

Vim側では、ウィンドウを閉じるとき

CloseMozilla

を呼んで自分で状態管理していただく必要があります。 まだリサイズとかアップデートを実装していませんので使いづらいと思います。

10.7. ターミナルからtanasinnに対してコマンドを送りたい

以下のような文字列を、TTYデバイスに対して送ってください。

¥x1b]220;<コマンド>¥x07

tanasinnはこれをtanasinnコマンドとして解釈し、実行します。 コマンドはtanasinnが使用しているデコーダーの符号化方式と同じものを使用してエンコードされている必要があります。

たとえば、シェルから

echo -en "¥e]220;overlayecho Hello, World!¥a"

とタイプすると、tanasinnにはコマンド「overlayecho Hello, World!」が渡ります。

10.8. キーマッピングの設定

tanasinnはVim scriptにファジーに似せた感じの書式でマッピング操作ができるコマンドを提供します。
  • nmap [置換対象キーシーケンス式] [置換後のキーシーケンス式]

    ノーマルモードのマッピングを設定します。
  • cmap [置換対象キーシーケンス式] [置換後のキーシーケンス式]

    コマンドラインモードのマッピングを設定します。
  • 特殊なキー

    • リーダーキー

      • <Leader> - 設定値nmap_manager.mapleaderおよびcmap_manager.mapleaderの値

    • モードスイッチ仮想キー

      • <nmode> - ノーマルモード

      • <cmode> - コマンドラインモード

    • モディファイヤ2度押し

      • <2-Shift> - Shift key twice.

    • スワイプジェスチャイベント仮想キー(一部のプラットフォームのみ)

      • <SwipeLeft> - 左にスワイプ

      • <SwipeRight> - 右にスワイプ

      • <SwipeTop> - 上にスワイプ

      • <SwipeBottom> - 下にスワイプ

    • ピンチジェスチャイベント仮想キー(一部のプラットフォームのみ)

      • <PinchOpen> - ピンチオープン

      • <PinchClose> - ピンチクローズ

    • 回転ジェスチャイベント仮想キー(一部のプラットフォームのみ)

      • <RotateRight> - 時計回りに回転

      • <RotateLeft> - 反時計回りに回転

設定例 .tanasinn/tanasinnrc
 ##############################################################################
 #
 # Set Leader keys mappings
 #

 # set <Leader>
 set nmap_manager.mapleader="<C-s>"
 set cmap_manager.mapleader="<C-s>"

 ##############################################################################
 #
 # Gesture mappings
 #

 #for tmux
 nmap <SwipeLeft>    <C-b>p
 nmap <SwipeRight>   <C-b>n
 nmap <SwipeDown>    <C-b>%
 nmap <SwipeUp>      <C-b>!
 nmap <RotateLeft>   <C-b><Space>
 nmap <RotateRight>  <C-b><Space>

 # zoom in/out
 nmap <PinchOpen>    <cmode>increase<CR>
 nmap <PinchClose>   <cmode>decrease<CR>

 ##############################################################################
 #
 # <Leader> mode
 #

 # zoom in/out
 nmap <Leader>, <cmode>decrease<CR><Leader>
 nmap <Leader>. <cmode>increase<CR><Leader>

 # move window
 nmap <Leader>h <cmode>left<CR><Leader>
 nmap <Leader>j <cmode>down<CR><Leader>
 nmap <Leader>k <cmode>up<CR><Leader>
 nmap <Leader>l <cmode>right<CR><Leader>

 # break <Leader> mode
 nmap <Leader><Esc> <nmode>

11. 既知の問題

  • Windows + Cygwin 環境では起動やエミュレーションがやや遅いです。

  • キーボードがSandS(Space and Shift)環境だとspace + space で shift + shift を検出してしまいます。代替手段を用意しなければいけません。

  • NIC無効時には動きません。

  • 開発版につき無駄に何回もloadScriptします。メモリ食いすぎ。

  • Python3未対応。Python2系が必要。

12. 拡張モジュール開発

12.1. tanasinnにおける「拡張モジュール」と「プラグイン」

拡張モジュールは、ファイル単位で認識され、所定のタイミングで実行されるJavascriptコードです。 Vimperatorにおけるプラグインと似たような概念です。 tanasinnにおけるプラグインは、拡張モジュールによりエクスポートされ、必要に応じてロードしたり、すきなとき にアンロードできるソフトウェア部品です。プラグアンドプレイのための一定の仕様を満たしたソフトウェア的実体、 という感じです。tanasinnバージョン0.1.5.4からは、プラグインマネージャが下図のように依存関係を追跡して 管理しますので、プラグインの動的な有効化・無効化がかなり安全に行えるようになっています。

images/pluginview.png

12.2. 拡張モジュール開発に向いている人

以下のような方はぜひ拡張モジュールを書いてみてください。

  • Mozilla環境のCUI/TUI的エンハンスに興味を持たれている方

  • TUI環境のGUI的エンハンスやWebとの融合に興味を持たれている方

  • 端末エミュレーションマニアでVTやTektronixやWyse端末やSixelを実装するのが好きな方

12.3. 拡張モジュール作成 初級編チュートリアル

tanasinnの基礎概念の習得からコンポーネント作成を経て、セッションプラグイン作成まで、なるべく飛ばないように
順を追って説明していきます。
注:現在、チュートリアルを書いていて、「この説明だるい、まどろっこしい、名前が良くない」と思ったところは思い
   切ってプログラムの方を変更して単純化していますので、サンプルコードがかなりバージョンセンシティブ化しています。
   最新版のtanasinnでお試しください。

12.3.1. チュートリアルの方針

このチュートリアルでは、簡単なtanasinnプラグインの作成過程を、なるべく平易な形で説明していきたいと思います。
初級編なので、「覚えることを最小限に」をモットーとしたいと思います。
拡張モジュールを書くためにtanasinnコアのソースをいきなり読みはじめるのはおすすめしません。
読むと精神的にやられます。
あのような書き方をせずとも、実はそこそこクリーンなJavascriptコードでプラグインを記述することができます。
記述は少し冗長になりますが総合的にみてずっと生産的です。
本チュートリアルではクリーンで単純なサンプル提示を心掛けます。

12.3.2. イベントバスの概念

チュートリアルに入る前に、これだけはどうしても触れておかなければなりません。「イベントバス」です。
「覚えることを最小限に」をふまえたうえでも、この説明だけはちょっとはずせません。
tanasinnは結合度的にかなりゆるいタイプのコンポーネントフレームワークを基盤として
構築されており、コンポーネント間のコミュニケーションは基本、同期的なメッセージ指向です。
そのメッセージ(セマンティクス上はイベントだったりコマンドだったりget関数だったりする)は、下図に示されるような
階層化されたバス上を流れています。
images/busmodel.png
Figure 27. tanasinn Bus Model
階層的に構築されたLANのようなものをイメージしてもらえるといいと思います。
ソフトウェアパターン的には、Observerパターンを階層化したもの、ととらえるとわかりやすいかなと思います。
このような階層において子要素を持つもの、上図においては、Process、Desktop、Sessionのようなものが、イベントを管理しています。
tanasinnハッカー達はこれを「イベントブローカー」、あるいは単に「ブローカー」と呼んでいます。
また逆にこの親要素にぶらさがっているもの、上図においてはProcess以外のノードを、「コンポーネント」と呼んでいます。
たとえば上の図でいうと、DesktopとSessionはイベントブローカー兼コンポーネントということになります。
コンポーネント「tty」はSessionというイベントブローカーを介してコンポーネント「screen」にメッセージを送ったりできるわけです。
イベントはUIウィジェットにおけるそれのようにキャプチャリングされたり、バブリングしたりすることはなく、
必要なら明示的にルーティングしてやる必要があります。
またVimperator/KeySnail使い的にはやや戸惑うかと思われますが、ブローカーはその子供(コンポーネント)に対する参照をもちません。
たとえばSessionというオブジェクトから「tty」への参照を明示的にひっぱってくることができません。
ややめんどくさいですがtanasinn全体がこのようなルールで構築されています。

12.3.3. 準備

まず、お使いのFirefox/Thunderbird/SeaMonkey等にtanasinnを導入してください。
以下の説明はtanasinn 0.1.5.4以上の環境を想定しているので、tanasinnは最新版を入れておいてください。
$HOME/.tanasinnがすでにある場合、以降の説明がtanasinnの実際の動きと食い違うことがあるかもしれませんので、
リネーム等しておくと、はまらなくてよいです。

12.3.4. サンプル1 - エントリーポイント

以下のようなサンプルを作成してください。

sample1.js

function main(broker) {
  broker.window.alert(broker);
}
これを、
%USERPROFILE%¥.tanasinn¥modules¥session_components¥ (Windows)
$HOME/.tanasinn/modules/session_components/ (Mac/Linux/FreeBSD)
につっこみます。フォルダ(ディレクトリ)は無いと思うので作成してください。
この状態でターミナルセッションを開始してみます。
こうなります。
images/sample1.png
ここでもし余裕があればalert関数の中身を変えるなどして、いろいろ実験してみてください。
聡明な方なら以下のことがわかると思います。
  • このプラグインモジュールのエントリポイントはmainというシンボルをもつ関数。引数にはSessionオジェクトが与えられている。

  • このプラグインモジュールはセッションが開始されるタイミングで読み込み直され、変更等も反映されている。

  • chromeウィンドウオブジェクトはセッションオブジェクトのwindowプロパティでとれる。

    Sessionオブジェクトはセッションバスを管理するブローカーです。
    ターミナルウィンドウがひとつ立ち上がるごとに、Sessionオブジェクトが生成されています。
    windowやdocument等のDOMオブジェクトがコンテキストに入ってきていないことにも注意してください。

12.3.5. サンプル2 - イベントとトピック

今度はセッションバス上を飛び交うメッセージを補足し、他のプラグインとコミュニケートしてみます。簡単です。
sample2.js

function main(broker) {
  broker.subscribe("event/before-input", function(message) {
    broker.notify("command/report-overlay-message", "key pressed: '" + message + "'");
  });
}
下図のように、キーボードを押すたびにオーバーレイインジケータが出てくると思います。
images/sample2.png
これは、「input」というコンポーネントからメッセージを受けとり、プラグイン「Overlay Indicator」にデータを
渡しています。"event/before-input" や "command/report-overlay-message" はイベントソースを識別するた
めの文字列で、「トピック」と呼ばれています。

12.3.6. サンプル3 - デバッグ環境をととのえる

つぎのサンプルにとりかかる前に、デバッグ環境を補強しましょう。
コンソールを有効化します。
enable console
saveprofile
images/enableconsole.png

起動してみましょう。

console
images/console.png

すこしコードも書いてみましょう。

sample3.js

function main(broker) {
  broker.subscribe("event/before-input", function(message) {
    throw "an error occured !!";
  });
}
なにかキーボードを押して"event/before-input"イベントを発生させるたびに、エラーが補足されます。
こうなります。
images/sample3.png
例外が自動補足されるということは、例外がコンポーネント境界を超えて伝播されないということでもあり
(実際は伝播させる方法もありますが)、tanasinnは基本、エラーに対してとてもルーズな
立場をとります。こまめにコンソールをチェックして対処するというケースが多いです。
テスト機構が完成すれば事情はもうちょっとましになりますが。
さきほどconsoleを有効化しましたが、通常使用時にはパフォーマンスを下げるので無効化しておいたほうが良いです。
consoleがなくても、ランタイムディレクトリ以下のlog/tanasinn-js.logを見ればエラー等を診断できます。
disable console
saveprofile

12.3.7. サンプル4 - コンポーネントオブジェクトの作成

コンポーネントを作成しましょう。
コンポーネントはプラグインの一歩手前の、プラグアンドプレイをサポートしない原始的なオブジェクトです。
tanasinnの規約によるとコンポーネントの最小要件は、以下のとおりです。
.英数字及びアンダースコア(_)、ハイフン(-)、ドット(.)の組み合せで表現されるString型のidをもつ。
.メッセージ "get/components" に対して、自分自身を返す。
コードで表現してみましょう。
sample4.js

function main(broker) {
  var plugin = {
    id: "sample"
  };
  broker.subscribe("get/components", function(message) {
    return plugin;
  });
}
コンポーネントの登録に相当する部分です。
登録というよりも、コンポーネントいるかーとか、メニューアイテムいるかーとか、バスに対して訊いてみて、
だれかが反応するような感じです。tanasinnではだいたいの事柄がそんなふうに表現されています。ほとんどがメッセージです。
ただし、idやenabledというシグネチャ等を要求することもあるので部分的にインターフェース指向でもあります。
今回のサンプルは他のコンポーネントとおしゃべりするための準備だったのですが、このままだと達成感ゼロなので、
最新ビルドにはlscomponentというコマンドを入れておきました。
何もしないコマンドなのですが、補完でコンポーネントを列挙します。
これです。
images/sample4.png
今回作成したsampleが、ちゃんとコンポーネントとして認識されています。
軽く100を超えるコンポーネントからtanasinnが構成されていることがわかりますね。
----- つづく。

12.4. 拡張モジュール作成 中級編チュートリアル

tanasinnが提供する基礎的なサービスである、イベント式、遅延構築テンプレート、ローカライズ機構、フック機構等の意義と構造を説明し、それらを使用してシーケンスハンドラ、コンプリーター、補完表示ドライバ、エンコーダー・デコーダー、メニューアイテム等、実用的なコンポーネントを作成していく手法を例示します。これらのツールや概念を覚えるとtanasinn拡張を美しく優雅に書くことができますが、tanasinn以外ではまったく役に立ちません。

----- coming soon.

12.5. 拡張モジュール作成 上級編チュートリアル

tanasinn向けにデザインされたMOPシステムtupbaseの概要と、その拡張法について説明します。コアモジュールを読みたい・いじりたい人向け。デスクトップ拡張やプロセス拡張、独自端末モード拡張等についてもここで触れます。これもtanasinn以外ではまったく役に立ちません。

----- coming soon.

13. Developing materials

13.1. Hierarchical event broker pattern

images/hebp.png

13.2. Event bus

images/busmodel.png

13.3. System stack

images/systemstack.png

13.4. Object system

Coming soon.

13.4.1. Class

13.4.2. Processing decorated (annotated) member name

13.4.3. Attributes

persistable
watchable
listen
subscribe
sequence
command
nmap
cmap
add-hook

13.5. process.js / Plug-and-play dynamic module loading

Coming soon.

13.6. event.js / Event system

Defines class EventBroker, which is able to interpret and evaluate "Topic" and "Event Expression".

13.6.1. "Topic" Overview

In this event system, registered events is concerned by "topic" that
is string variable consists of some digits and alphabets.
If you registered an event as follows:
var id = broker.subscribe(<topic>, <handler>, ....);
This event handler could be fired as following code:
broker.notify(<topic>);
The subscribe method returns an ID string.
When you want to unregister handler, use it as follows:
broker.unsubscribe(id);
NOTE that this ID is NOT corresponds to a handler, one-to-one.
The relation between Topic-ID is as same as that, because this event
system is based on the concept of multicast delegate system.
images/topic.png

13.6.2. "Event Expression" Overview

Event Expression indicates how the broker waits multiple events.
It was given to subscriber method, that called such as:
broker.subscribe(<expression>, ....);
<expression> consists of 1 or multiple event topics and other tokens,
that ruled by simple grammar. the detail of it is as below...

13.6.3. Grammer of Event Expression

   IdentifierCharacter := '_' | '-' | digit | alphabet

   NormalToken := IdentifierCharacter, IdentifierCharacter*

   GlobToken := '{', NormalToken, '}'
              | '{', NormalToken, '}', NormalToken
              | NormalToken, '{', NormalToken, '}'
              | NormalToken, '{', NormalToken, '}', NormalToken

   GlobToken := NormalToken | GlobToken

   PrimaryExpression := Token | '(', Expression, ')'

   UnaryOperator := '@' | '~'

   UnaryExpression := PrimaryExpression | UnaryOperator, PrimaryExpression

   BinaryOperator := 'and' | 'or' | '&' | '|'

   BinaryExpression := UnaryExpression | UnaryExpression, BinaryOperator, UnaryExpression

   Expression := BinaryExpression

13.6.4. Event Expression Example

  • case 1.

     "A"
-> it triggered when the event "A" is signaled.
  • case 2.

     "A | B"
-> wait for multiple events "A" OR "B".
   it is triggered when one of either events is signaled.
  • case 3.

     "A & B"
-> wait for both events "A" AND "B"
   it is triggered when both of these events is signaled.

13.7. ttydriver.py / TTY Driver

13.7.1. Module Overview

This module is assumed to be called by tanasinn’s "tty.js". First, tanasinn opens TCP channel and listen on 2 ports, [I/O channel] and [Control channel]. Next, tanasinn should call this script, with 2 arguments that is represent above-mentioned 2 channel’s port numbers. Then, A TeletypeDriver object is instantiate. it connect these channels and establish TCP connection, and finally forks new 4 processes as follows:

  1. Application Process This is user-specified application process, launched by command such as the following.

      /bin/sh -c 'exec <startcommand>'
<startcommand> is asked for tanasinn through the <Control channel>.
  1. Writing Process This process wait to receive data from tanasinn through <I/O channel>, which is user’s input key sequence in many cases. As receiving data, the Process passes it to TTY master device as it is.

  2. Reading Process This process wait to receive data from TTY master device, which is output sequence from application program in many cases. As receiving data, the Process passes it to tanasinn through [I/O channel].

  3. Controlling Process This process communicate with tanasinn through [Control channel] in simple, lightweight, 7bit ascii-based protocol.

images/topic.png
figure-1. Communication among TTY device pair, TeletypeDriver, tanasinn.

13.7.2. About [Control channel]'s protocol

Protocol Overview
  1. This protocol is line-oriented. line terminator is \n (0x0a).

  2. This protocol is command-based. 1 line should be interpreted as 1 command. A command is composed of 1 or multiple tokens. delimiter character is ' ' (0x20).

  3. First token is opecode, represent a operation. An opecode consists of lower-case alphabetic sets ([a-z]+).

  4. Tokens after opecode represent arguments. An arguments consists of multiple printable characters, that is encoded in base64 Data Encoding, defined in RFC-3548.

       example 1:
         xoff\n
Opecode of this command is "xoff". "\n" is line terminator.
       example 2:
         resize ODA= MjQ=\n
In this case, [bold]*opecode* is "resize".
Arguments are "ODA=" and "MjQ=", these strings mean "80" and "24"
when decoded.

13.8. builder.js / XUL dynamic construction

Accept UI-template object and convert it to a tree of DOM element.

13.8.1. UI-template overview

UI-template is JSON-style object that is described as based on the
specific rule.
It is to be converted to a node set of XUL/HTML/SVG elements by
"Template Builder" object.
Dependencies between DOM fragments are automatically resolved.
You need not to take care of order for building them.

13.8.2. Examples

Example 1:


   ({                          // <--- (1)
     tagName: "box",           // <--- (2)
     flex: 1,
     style: {                  // <--- (3)
       backgroundColor: "red"  // <--- (4)
     }
   })
It will be converted to following XUL element.
     <box flex="1" style="background-color: red;"/>
Every UI-template object consists of <Element node> (1), and it is
typically a tree set of <Element node>s.
It always includes "tagName" property (2), and often includes
<Style node> (3).
<Style node> may have some CSS properties in camel-case (4).

Example 2:


   ({
     tagName: "html:div",
     parentNode: "id-of-parent", // <--- (5)
     style: {
       textAlign: "center"
     }
     childNodes: {               // <--- (6)
       tagName: "html:div",
       style: {
         fontSize: "1.5em"
       },
       childNodes: [             // <--- (7)
         {
           tagName: "html:span",
           innerText: "abcde"    // <--- (8)
         },
         {
           tagName: "html:div",
         }
       ]
     }
   })
It will be converted as below.

   <html:div style="text-align: center;">
     <html:div style="font-size: 1.5em;">
       <html:span>abcde</html:span>
       <html:div/>
     </html:span>
   </html:div>
The "parentNode" property (5) indicates at which the current node is to
be set. if the type of property value was "string", it regards as node
ID of the parent node. or if its type is "object", especially
HTMLElement, it regards as the parent node object itself.
The "childNodes" property has an effect which cascades down
following <Element node> (6). If you wanted to create two or more
child nodes under the same node, you should substitute an Array of
<Element node>s (7) in "childNodes" property.
The "innerText" property (8) creates a text node under current
<Element node>.

Example 2:


   ({
     tagName: "hbox",
     id: "parent",
     flex: 1,
     onconstruct: {              // <--- (9)
       handler: function() { window.alert(this.id); }
     },
     childNodes: [
       {
         tagName: "vbox",
         id: "left",
         flex: 1,
         listener: {             // <--- (10)
           type: "mouseover",
           context: window,
           handler: onMouseOver
         }
       },
       {
         tagName: "vbox",
         id: "right",
         flex: 1,
         listener: [
           {
             type: "mouseup",
             handler: onMouseUp
           },
           {
             type: "mousedown",
             handler: onMouseDown
           }
         ]
       }
     ]
   })
It almost corresponds to following XUL code.

   <hbox id="parent" flex="1">
     <vbox id="left" flex="1" onmouseover=""/>
     <vbox id="right" flex="1"/>
   </hbox>
   <script type="application/x-javascript"/>
     <![CDATA[
       var parent = document.getElementById("parent");
       var left = document.getElementById("left");
       var right = document.getElementById("right");
       (function() window.alert(this.id)).apply(parent);
       left.addEventListener("mouseover", function() {
         onMouseOver.apply(window, arguments);
       }, false);
       right.addEventListener("mouseup", onMouseUp, false);
       right.addEventListener("mousedown", onMouseOver, false);
     ]]>
   </script>
The "onconstruct" property (9) defines a event function which fired
when the property itself is parsed.
The "listener" property (10) attaches a event handler at the current
<Element node>.

14. バグレポート

バグレポートを歓迎します。 saitoha <user@zuse.jp> twitter: http://twitter.com/kefir_