charmapの続きなど

追記_2012/11/01/_10時頃

http://en.wikipedia.org/wiki/Box-drawing_character
試してませんがもしこのwikipediaのページで書かれているbox-drawingとblock-elementsの半角のフォントを用意できるのならばそれを使用して、charmapにはからの範囲までを半角と定義するか、全角にならないようにコメントアウトなり消すなりして使っていったほうがいいような気がします
もしそうするならばこの記事ではxtermにpatchを当てるだけで済むと思います
ちなみにフォントが全角なのにcharmapだけを半角にすると一部の文字が見えませんでした…。

何をするのか

前回で

    <U03C3>...<U03C9> 2
    <U2502> 2
    <U251C> 2
    <U2524> 2
    <U252C> 2
    <U2534> 2

これらが含まれているとαなどが半角になってしまうと書きましたが含まれていてもxterm上でちゃんと動くようにします
ついでにtmuxで上下に分割した時に出る境目の文字が全角なのに半角の場合と同じ個数で表示されて画面がずれるのを修正します

追記1_2012/10/26_10時頃:alsamixerなどで罫線を使っている場合もずれてしまうのでterminfoもいじります

やり方

XTerm(281)を想定しています

1:xterm

参考にしたサイトの1つ目を参考にしながらそのままやると

    <U03C3>...<U03C9> 2

これが含まれているとダメになってしまうのでさらにこれが含まれていると弾くものを消せばいいのですがそれも消すと関数の中身がなくなるので
xtermのソースファイルのxterm-281/util.cにある

※実際には行番号は書いてありません
    4250 static Bool
    4251 systemWcwidthOk(int samplesize, int samplepass)

この関数を全て消します
それとこの関数は

※実際には行番号は書いてありません
    4317     if (xtermEnvUTF8() &&
    4318         systemWcwidthOk(xw->misc.mk_samplesize, xw->misc.mk_samplepass)) {

ここから呼び出されているのでこのif文を

※実際には行番号は書いてありません
    4317     if (xtermEnvUTF8()){

このように書き換えて4318行目を消せばsystemWcwidthOkはなくても問題は生じなくなるので後はコンパイルするだけです

これで最初に書いた文字コードを全角にしてもxterm上では想定通りの動きをするようになりました
わざわざブロックしていたし…少し怖いですね

2:tmux

このまま使っても問題ない人もいると思いますが
tmuxで使うとborderというのかなんというのかわかりませんがペインの境目の線がさっき書いたようにずれるのでずれないようにします
ずれないようにといっても幅が2の罫線を使うのはソースを相当修正しないとできないと思うので諦めて(途中で書くのを諦めたpatchは後で貼ります。誰か完成させてください)
この境目を半角の別の文字に割り当てます
tmux1.6だと

    tmux-1.6/tty-acs.c

このソースファイルの

    const struct tty_acs_entry tty_acs_table[]

に書いて有ります
以下、対応表

注意:罫線しか書いていません
x
q
l
k
m
j
w
v
t
u
n

そういうわけで上下に分割した時に出る境目の文字を「-」に変更したければ

    { 'q', "\342\224\200" },

これを

    { 'q', "-" },

このように書けばいいです
マルチバイトの文字を指定したい場合は書いてあった通りに8進数で書かないといけないのかもしれません
それで私は罫線を
xは「|(パイプ)」、qは「-(ハイフン)」、それ以外は「+」にしました
色を表示できる環境ならば罫線をスペースにしてしまって
~/.tmux.confに

    set -g pane-active-border-bg cyan
    set -g pane-active-border-fg cyan
    set -g pane-border-bg white
    set -g pane-border-fg white

このように色をつけてしまってもいいかもしれません
スペースじゃなくても同じ半角ならばこのようにfgも書けば同じ見た目です
しかし横線と縦線の比率があっていないので若干気持ち悪いです

3:terminfo 追記1_2012/10/26_10時頃

alsamixerなどで罫線などを使っている場合もずれてしまうのでterminfoもいじります
参考にしたサイトの2つ目と同じ事をします
まず、ずれてしまう端末を起動した後に
作業ディレクトリに移動します
ちなみに${TERM}の種類ごとにいじる必要があるのでtmuxなしのxtermとtmuxありのxtermを普段使っている人は2回ほど繰り返しやります

    % infocmp > 好きな名前のファイル

で出力を保存して、その出力したファイルの (${TERM}がscreen-256colorだった場合の例)

    acsc=++\,\,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
    rmacs=^O,
    smacs=^N,

この3種類の変数とその値を綺麗サッパリ消し去って保存します
後は

    % tic さっき保存した好きな名前のファイル

これで完了です
他にも乱れてしまう${TERM}を使っている場合は繰り返し行います

終わり

tmuxの書くのを諦めたpatch

tmux1.6用です

diff -Narup tmux-1.6.orig/screen-redraw.c tmux-1.6/screen-redraw.c
--- tmux-1.6.orig/screen-redraw.c   2012-10-14 23:58:08.946579088 +0900
+++ tmux-1.6/screen-redraw.c    2012-10-14 23:58:26.699828916 +0900
@@ -19,6 +19,8 @@
 #include <sys/types.h>
 
 #include <string.h>
+#include <locale.h>
+#include <wchar.h>
 
 #include "tmux.h"
 
@@ -173,8 +175,10 @@ screen_redraw_screen(struct client *c, i
    struct tty      *tty = &c->tty;
    struct window_pane  *wp;
    struct grid_cell     active_gc, other_gc;
-   u_int            i, j, type;
+   u_int            i, j, type, k, cw, old_type;
    int          status, fg, bg;
+    const char *acs;
+    wchar_t *wacs;
 
    /* Suspended clients should not be updated. */
    if (c->flags & CLIENT_SUSPENDED)
@@ -208,18 +212,43 @@ screen_redraw_screen(struct client *c, i
    colour_set_bg(&active_gc, bg);
 
    /* Draw background and borders. */
+    setlocale(LC_CTYPE, "");
    for (j = 0; j < tty->sy - status; j++) {
        if (status_only && j != tty->sy - 1)
            continue;
-       for (i = 0; i < tty->sx; i++) {
+       for (i = 0, k = 0, cw = 0, old_type = 0; i < tty->sx; i++) {
            type = screen_redraw_check_cell(c, i, j);
            if (type == CELL_INSIDE)
                continue;
+            else if (type == CELL_JOIN ||
+                    type == CELL_BOTTOMJOIN ||
+                    type == CELL_TOPJOIN ||
+                    type == CELL_TOPBOTTOM ||
+                    type == CELL_RIGHTJOIN ||
+                    type == CELL_BOTTOMRIGHT) {
+                if ((cw == 0) && (old_type != type || old_type == 0)){
+                    old_type = type;
+                    acs = tty_acs_get(tty, CELL_BORDERS[type]);
+                    if (acs != NULL){
+                        wacs=(wchar_t*)xmalloc(sizeof acs);
+                        mbstowcs(wacs,acs,sizeof wacs);
+                        cw = wcwidth(*wacs);
+                        xfree(wacs);
+                    }else
+                        cw = wcwidth(CELL_BORDERS[type]);
+                }
+                if (i % cw != 0){
+                    k++;
+                    if (type == CELL_TOPBOTTOM)
+                        continue;
+                    k+=i%cw;
+                }
+            }
            if (screen_redraw_cell_border1(w->active, i, j) == 1)
                tty_attributes(tty, &active_gc);
            else
                tty_attributes(tty, &other_gc);
-           tty_cursor(tty, i, j);
+           tty_cursor(tty, i - k, j);
            tty_putc(tty, CELL_BORDERS[type]);
        }
    }

上下に分割したら出る境目がとりあえず(2行目に行か|折り返しし)ないようにしたpatchです
罫線が半角2個分の時でしかデバッグしてません
ちなみに左右に分割したらその境目が半角1個分から2個分に変わっているのでずれます。直す処理は書いていません
それとtmux上では罫線は半角として処理をしているようで(?)そこも直さないとコピーモードに移行した時にカーソルの動きがおかしかったり画面がさらにずれたと思います
それと縦の境界線に全角を使ってしまうと半角1個分を失うことになって画面が狭くなります
そういった面倒くさい、悲しい気持ちになることから私はこのpatchを使わないことにしました

本当はこういう境界線を内部データ的に全角で扱いたかったけどどうすればいいのかわからなかったんです

具体的にこのpatchの処理の解説

まず上下に分割をした時に出そうな罫線のタイプだった場合に処理を実行します(なんとなく見たこともないタイプも入れてますけど…)
幅が一度も取得されていそうになかったりそのタイプが変更されていた場合に文字の幅を取得しにいきます
取得し終えたらその幅を使ってはみ出してしまう分だけ文字を削ります
しかし、ただの横線以外(角など)の場合はうまいこと削られないように、なおかつはみ出ないように調整しているはずです

charmapをいじったのにtmuxで半角として扱われる文字がある問題

恐らくtmuxに独自の判定をする関数があるせいだと思われます

    tmux-1.6/utf8.c

ここにあるのがそうです
wcwidth()関数を使えばこの問題は解決できそうなので間違っていなければ気が向いた時に書きますが
我先にと書いて貰ったほうがありがたい

後書き

プログラミングで初めてmallocを使ったのはここだけの話。(実際はxmallocっていうtmux-1.6/xmalloc.cで定義されているものだけど)
とりあえずutf8の幅が曖昧な文字を全て全角にできて*1w3mでテーブルタグを正常に表示できて、なおかつtmuxの境界線が正常に表示できて今のところ満足

*1:✕など一部の文字はNと定義されているから半角のままにしてあるけど…