screen + curses で表示がずれるよ、というお話

もうカラムの話は飽きたかも知れませんが今回も UTF 的なナニがソレのカラムの話です。
とりあえずボクは飽きました。


curses は罫線が1カラムであることを期待します。常識的に考えてそうであるべきです。し、地球上のほとんどの環境では1カラムです。
が、日本で使われてるフォントの多くは2カラムで出力します。誰がそうしたのかしりませんが、小石くらいは投げてもいいと思います。
(勿論ここに行き着く前に罫線は Ambiguous なため、CJK周りの煩雑な問題を解決(になってないけど)するパッチを当てないといけません)
どういうことか分からない人は以下のURLを開いてみましょう。
http://en.wikipedia.org/wiki/Box_drawing_characters
多分ページ内のテーブル内の罫線は全角、画像の罫線は半角に見えると思います。ギャグだと思いたい。
そんなわけで、普通に curses を使ったソフトを使うと表示が盛大にずれてしまいます。
mlterm ではこういった問題を回避するために「curses などで罫線を出力する時」だけ特別に 1 カラムの罫線を出力してくれます。なんて賢いんだ…!天…才…!
さてここまではよろしい。ここから screen ユーザー涙目な話になります。


mlterm を使っていても screen を介すと台無しになります。何故かというと screen が(端末エミュレータでもあるので当然ですが)自前でエスケープシーケンスを処理してくれるからです。具体的にどういった処理がされるかというと、当然ですが罫線に普通に置き換えられます。
これは日本以外の環境では1カラムなので問題ないので順当な処理ですが、日本人は悲しい気持ちになります。
mlterm も既に罫線に置き換えられたものはさすがにどうしようもないのでそのまま表示してずれます。泣いていい。


でまあそこから、「screen に ESC(0 を処理させずに mlterm に処理させる」とか「Unicode に半角の罫線とかその代わりになるいい感じの文字あるんじゃね」とか色々やったんですが、ボクが悪かったのか何が悪かったのか結局どれも駄目で、個人的な結論としては普通に「-+|」に置き換えよう、という話になってしまいました。
それが嫌だから1週間頑張ったんですけどね!
もしそんなダサい形でも直したいという酔狂な人がいたら encoding.c の「/* 0: special graphics (line drawing) */」とコメントしてある辺りを以下のように適当に書き換えましょう。

  { 0x30, 0 },		
  { 0x005f, 0x25AE }, { 0x0060, 0x25C6 }, { 0x0061, 0x2592 },
  { 0x0062, 0x2409 }, { 0x0063, 0x240C }, { 0x0064, 0x240D },
  { 0x0065, 0x240A }, { 0x0066, 0x00B0 }, { 0x0067, 0x00B1 },
  { 0x0068, 0x2424 }, { 0x0069, 0x240B }, { 0x006a, 0x002B },
  { 0x006b, 0x002B }, { 0x006c, 0x002B }, { 0x006d, 0x002B },
  { 0x006e, 0x002B }, { 0x006f, 0x23BA }, { 0x0070, 0x23BB },
  { 0x0071, 0x002D }, { 0x0072, 0x23BC }, { 0x0073, 0x23BD },
  { 0x0074, 0x002B }, { 0x0075, 0x002B }, { 0x0076, 0x002B },
  { 0x0077, 0x002B }, { 0x0078, 0x007C }, { 0x0079, 0x2264 },
  { 0x007a, 0x2265 }, { 0x007b, 0x03C0 }, { 0x007c, 0x2260 },
  { 0x007d, 0x00A3 }, { 0x007e, 0x00B7 },
  { 0, 0}, 

ちなみに大抵のツールは「curses による罫線の出力にASCII文字を使う」みたいなオプションが存在すると思いますが、まあないのもあるかもしれないのでこちらのほうがいい、と思います。
自分で書く時にわざわざあほなオプションつけなくていいし…


まあまさか1週間以上時間かけてこんなしょんぼりした結果を向かえるとは自分でも思ってなかったので正直がっかりです…
端末周りの知識が足りず(エスケープシーケンスってなんぞー)コード読むのにめちゃくちゃ時間がかかってしまいました。得られた物は所謂バッドノウハウとか化石とかに近いものだと思うので、ちょっと悲しいですね。
なんにせよもう Unicode まわりの煩雑な問題には二度と関わらないようにして、これからは幸せな人生を歩みたいと思います。ありがとうございました。



追記:ちなみにこの辺の曖昧な問題に対する回答として「エスケープシーケンスでカラム数を指定してやろう」という案があって、以下にちゃんとした提案としてまとまっています。
http://www.cl.cam.ac.uk/~mgk25/ucs/scw-proposal.html
が、今回のような「1カラムが期待される文字に大して2カラムの文字しか用意されていない」場合、そもそも端末側では解決することができません。ないものはないんです。
前回の「BULLETが本来1カラムなのに2カラムで計算されちゃってずれるよー」という逆の問題はそれでいけるのですが…
幸せ逃しそうなのでこの辺で止めます。


追々記:何か結局悔しくて続けてたんですが、普通に実装できました。よかったですね!無視するんじゃなくて、出力すればいいじゃんという感じで…
ansi.c の DesignateCharset で

  if (c == '0' && n == G0)
    {
      AddStr("\033(0");
      linedrawing = 1;
      return;
    }
  if (c == 'B')
    {
      if (linedrawing && n == G0)
        {
          AddStr("\033(B");
          linedrawing = 0;
          return;
        }
      c = ASCII;
    }

とか。linedraw はグローバル変数として適当に宣言。
デフォルトだと as ae が ^N ^O だったりするかもしれないので適当に termcap なり terminfo なりごにょってください。おつかれさまでした。ありがとうございました。