代替画面バッファ 2

今回は代替画面バッファに関する雑多な事を書いてみようと思います。
普通のユーザーには役に立つ事はほとんどないと思いますが、こんな事も有るんだと読んで貰えたら幸いです。

3種類の代替スクリーン切り替え命令

vi等の全画面を使うプログラムを実行した時、終了した時に実行前の画面状態に戻したいという要望から代替画面バッファは生まれました。
この機能はxtermでの独自拡張として生まれました。そしてxtermで使われている内に、いくつか不具合が見つかってきました。この不具合に対処する為に、xtermは既存の命令の動作を変更するのではなく、動作が少し違う新しい命令を新設するという選択をしました。その為、現在では3種類の代替画面切り替え命令が存在します。

端末のモード

ここで、代替画面切り替え命令のベースになっているモード切り替えについて触れます。

端末には、”キーボードロック”や”挿入モード”、”自動改行”のように、状態が主にon/offで表される機能があり、これらを総称してモードと呼びます。
端末の制御シーケンスの標準である ECMA-48 では、これらのモードを設定/解除する命令として、SM(Set Mode)/RM(Reset Mode) が定義されています。SM/RMの形式は以下のようになっています。

SM:  CSI Pm h
RM:  CSI Pm l

CSI は ESC(0x1b) [ の2文字の並びです。8ビットの制御シーケンスを受け付ける端末では、CSI(0x9b) も使えます。
Pm は、”キーボードロック”ならば2、”挿入モード”ならば4というように、機能に対応した数値を指定します。; で区切って複数の機能を指定する事も出来ます。
例えば、ESC [ 2 h を送ると端末はキーボードをロックして入力を受け付けなくなり、ESC [ 2 l を送信すると解除されます。

機能とモード番号の対応も ECMA-48 で定められていますが、vt100のメーカーで有るDECはこれに無い独自のモードを使う為に、独自の設定/解除命令として DECSET/DECRST を定義しました。DECSET/DECRST の形式は以下の通りです。

DECSET:  CSI ? Pm h
DECRST:  CSI ? Pm l

この DECSET/DECRST は、機能に適当な番号を振ればいい事と、対応していない端末に悪影響を与えにくい事から、DEC以外でも機能拡張に使われるようになりました。代替画面切り替え命令も DECSET/DECRST を利用しています。

切り替え命令

それでは、それぞれの切り替え命令を見ていきましょう。

47 — 代替画面切り替え

最初に作られた代替画面切り替えコマンドが、この47番です。termcap/terminfoの話でも書いたように、主に ti(smcup)/te(rmcup) で以下のように設定されて使われていました。

ti/smcup:  \E7\E[?47h
te/rmcup:  \E[2J\E[?47l\E8

それぞれでどのような事をやっているか分解して書いてみます。

ti/smcup:
  \E7      -- カーソル位置保存
  \E[?47h  -- 代替画面へ切り替え

te/rmcup:
  \E[2J    -- 画面消去
  \E[?47l  -- メイン画面へ切り替え
  \E8      -- カーソル位置復元

この切り替え命令ですが、使われている内に端末の機能で代替画面バッファを無効にしていると問題が出る事がわかりました。
te で代替画面の消去を行っているのですが、代替画面バッファを無効している状態でも画面消去が行われてしまい、viで編集していた内容やlessで表示していた内容が消去されてしまいます。これでは代替画面バッファを無効にした意味が有りません。

1047 — 代替画面切り替えと画面消去

そこで新たに作られたのが、この1047番です。代替画面への切り替え時については機能変更は有りません。変更があったのはメイン画面への切り替えの方で、切り替え前に代替画面の消去を行います。47番のtermcap設定例で、teで一緒に指定されている画面消去を取り込んだ形ですね。これによって、代替画面バッファを無効にした時に画面消去も一緒に無効になるようになりました。
termcap/terminfoでは、以下のように使われます。

ti/smcup:  \E[?1048h\E[?1047h
  \E[?1048h  -- カーソル位置保存
  \E[?1047h  -- 代替画面への切り替え

te/rmcup:  \E[?1047l\E[?1048l
  \E[?1047l  -- メイン画面への切り替え
  \E[?1048l  -- カーソル位置復元

1048 — カーソル位置の保存/復元

代替画面切り替え命令では無いですが、関連する機能として1047番と一緒に作られたのがこの1048番です。
代替画面からメイン画面に切り替えた時に、カーソル位置が元の場所に戻らないと続きから使えているようになりません。そこで、代替画面への切り替え前にカーソル位置を保存し、メイン画面に戻った時にカーソル位置を復元するという事を行います。
カーソル位置保存/復元の制御シーケンスは元からありました(DECSC/DECRC)が、代替画面バッファを無効にした時に一緒に無効にならないと問題が出るため、代替画面バッファ関連のカーソル位置保存/復元用として新設されました。

1049 — 代替画面切り替えと画面消去、カーソル位置保存

どうせ代替画面の切り替えとカーソル位置保存を同時に行うのだから、一緒にしちゃえと作られたのがこの1049番です。
代替画面への切り替え時は、カーソル位置保存→代替画面への切り替え→画面消去 の順番で行われ、メイン画面への切り替え時は、メイン画面への切り替え→カーソル位置復元 が行われます。
基本的には1047番と1048番を組み合わせた機能ですが、画面消去のタイミングがメイン画面への切り替え直前から代替画面への切り替え直後に変更になっています。これによって、メイン画面に切り替わった後でも、メニュー等で代替画面へ切り替える事によって代替画面の内容が確認できるようになりました。

termcap/terminfoでは、以下のように使われます。

ti/smcup:  \E[?1049h
te/rmcup:  \E[?1049l

ずいぶんすっきりしましたね。特にtermcapでは1エントリ1023バイトの制限が有るため、設定に必要な文字数が減るのはかなり助かります。

どれを使えばいいか

一番新しい1049番が実装されたのが14年前なので、現在普通に使われているxtermならば大抵はすべてに対応していると思われます。また、今時のxterm互換な端末エミュレータでもすべてに対応している事が多いでしょう。なので通常は1049番を使えばいいでしょうし、標準のtermcap/terminfoエントリでも1049番を使うように書かれている事が多いと思います。もしもtermcap/terminfoで47番を使うように書かれていた場合は、1049番を使うように書き換えた方がいいかもしれません。

最後に

雑多な事を書くといいながら、切り替え命令の種類の事だけになってしまいました。代替画面バッファに似た機能としてvt400/500のページについても触れようと思っていたのですが、眠いので次回にします。

You can leave a response, or trackback from your own site.

Leave a Reply

Subscribe to RSS Feed Follow me on Twitter!