fbtermで端末の色を指定できるようにする

前書き

xtermでいうとこの~/.Xresourcesに書くような

    XTerm*background: #000000
    XTerm*foreground: #ffffff
    XTerm*color0: #242424

こんな感じの部分を~/.fbtermrcで設定できるようにする
以前からこんな機能ほしかったけどfbterm自体あんまり使わないからかかなかった
でもまたお世話になりそうなんで書く
というか書かないとデフォルトの色は見にくい、今まで誰もやらなかったのが不思議なほどだよ

やり方

patchを書いたので
https://raw.github.com/silenvx/PKGBUILD/master/fbterm/config_colors.patch
ここから各自落として当てたら使えます
archlinux系の人は

    % git clone https://github.com/silenvx/PKGBUILD.git
    % cd PKGBUILD/fbterm
    % makepkg -i

で入る

~/.fbtermrcには

    color-0=000000
    color-1=952519
    color-2=69821b
    color-3=af8700
    color-4=3387cc
    color-5=a757a8
    color-6=418179
    color-7=a0a8b0
    color-8=444444
    color-9=ff5f55
    color-10=73d144
    color-11=ffd348
    color-12=89bdff
    color-13=d787ff
    color-14=71bebe
    color-15=c3c6ca

こんな感じで24bitの16進数で色を書いて0から15までの色を指定できる
どんな感じかはどっかに落ちてるであろう256colors2.plを使って確認するといい
但しtmux上でないと画面がバグるので注意
たぶんhttps://joeldotfiles.googlecode.com/svn/trunk/256colors2.pl このあたりに落ちてると思う

patchを書くためにしたソースコードとの戯れ方 (ここから下は読む必要ない)

既にbgとfgならば~/.fbtermrcにて

    color-background=0

こんな書き方が可能なのでこの変数名を使ってソースコード全体に検索をかけることにする

ソースコードを展開させて、そこをカレントディレクトリにしたのちfindする

    % find . -type f -exec grep -nH 'color-background' {} +
    ./src/screen_render.cpp:80:             Config::instance()->getOption("color-background", color);
    ./src/fbconfig.cpp:184:         "color-background=0\n"
    ./src/fbconfig.cpp:231:         { "color-background", required_argument, 0, 'b' },
    ./src/fbconfig.cpp:267:                         "  -b, --color-background=NUM      specify background color\n"
    ./src/fbshell.cpp:328:          Config::instance()->getOption("color-background", color);
    ./doc/fbterm.1.in:52:\fB-b, --color-background=\fR\fInum\fR

実際に値を使って何かしているのは

    ./src/screen_render.cpp:80:             Config::instance()->getOption("color-background", color);
    ./src/fbshell.cpp:328:          Config::instance()->getOption("color-background", color);

この2つなのでまずは上から見ていく

    void Screen::initFillDraw()
    (略)
        Config::instance()->getOption("color-background", color);
        if (color > 7) color = 0;
        bgcolor = color;

initFillDraw()関数内の処理のようだ、関数名にiniとdrawが使われていることから描写に関する初期化処理だろう
color-backgroundの変数には0から7の値のうちどれかを入れることになっているので
if文で判定して、範囲外なら0にしていることから察するに
検索でヒットした行はcolor-backgroundの値を取得しているようだ

この時に気づいたんだが、今見てるのは背景に関する処理っぽいし
文字と背景は処理違う気がしてきたのでcolor-foregroundで検索し直した

    % find . -type f -exec grep -nH 'color-foreground' {} +
    ./src/fbconfig.cpp:183:         "color-foreground=7\n"
    ./src/fbconfig.cpp:230:         { "color-foreground", required_argument, 0, 'f' },
    ./src/fbconfig.cpp:266:                         "  -f, --color-foreground=NUM      specify foreground color\n"
    ./src/fbshell.cpp:324:          Config::instance()->getOption("color-foreground", color);
    ./doc/fbterm.1.in:49:\fB-f, --color-foreground=\fR\fInum\fR

fbconfig.cppは見るからに設定ファイルに関する処理なのでで残った選択肢からfbshell.cppの方を見ると
このファイルの

    static const Color defaultPalette[NR_COLORS] = {

ここに256色の パレットカラーがあることに気づいたのでとりあえず~/.Xresourcesに書いてるのと同じ値を
0番から15番に記入してコンパイルして試しに実行してみると予想通り色が変更された
ちなみにxtermの場合は./256colres.hと./charproc.cで定義されているみたいだ
ソースコードまでは確認してないがxtermの場合はbgと256色の0番は違うのでfbtermもそれに倣ってpatchを書いていく
ひとまずは0番から15番までの色を~/.fbtermrcで指定できるように改変していく
仕様としては~/.fbtermrcに

color-0=000000
color-1=FF0000

みたいな感じで書いていけるようにする

まずは設定の追加方法を知るためにtermを指定できるようにするpatchが既にあるのでそれを参考にする
http://code.google.com/p/fbterm/issues/attachmentText?id=48&aid=480002000&name=fbterm-1.7-term.patch
それで見た結果、

    Config::instance()->getOption("term", term, sizeof(term));

こんな感じで呼び出すだけで設定が抜き取れそうだったのでさっそく改変して

    s8 hoge[32] = "hoge"
    Config::instance()->getOption("hoge", hoge, sizeof(hoge));
    setenv("HOGE", hoge, 1);

こんな感じのをsrc/lib/shell.cppのvoid Shell::createShellProcess(s8 **command)に埋め込んでみて
~/.fbtermrcにhoge=foobar的なのを書いて、% echo $HOGE でfoobarが出力されるのを確認した
けど最初にhogeを代入する必要があったのか疑問だな…~/.fbtermrcから設定を取ってくるときに未定義だとNULLっぽいし
そういうわけで後は

    static const Color defaultPalette[NR_COLORS] = {

からconstとって勝手に代入するようにするだけ
このColor型は

    % find . -type f -exec grep -nH 'Color' {} +|grep struct
    ./src/screen.h:29:struct Color {
    ./src/fbshell.h:82:     struct Color *mPalette;

src/screen.hで定義されていて

    struct Color {
        u8 red, green, blue;
    };

unsigned char型が3つ並んでるだけです
ここまで分かれば後はアルゴリズムは思いつきますが、どこに書けばいいのかわかりません
とりあえずdefaultPalette変数が使われてる前で書き換えさえすれば問題なさそうなので検索する

    % find . -type f -exec grep -nH 'defaultPalette' {} +
    ./src/fbshell.cpp:42:static const Color defaultPalette[NR_COLORS] = {
    ./src/fbshell.cpp:522:                  memcpy(mPalette, defaultPalette, sizeof(defaultPalette));
    ./src/fbshell.cpp:539:                  screen->setPalette(defaultPalette);
    ./src/fbshell.cpp:572:          screen->setPalette(mPaletteChanged ? mPalette : defaultPalette);
    ./src/fbshell.cpp:582:          screen->setPalette(defaultPalette);

それぞれの処理は

    void FbShell::request(RequestType type,  u32 val)
    void FbShell::switchVt(bool enter, FbShell *peer)

このどちらかの関数かで行われてる
それぞれの関数の最初の処理に青と緑だけ色をつける処理を書いてみたところ

    void FbShell::switchVt(bool enter, FbShell *peer)

こっちの方で書いた緑色が表示されたのでとりあえずこの関数の先頭に書き換える処理を書く

そうと決まればどんな風に書けばいいか想像する
まず取得する値は文字列になるので6byte
1byteの数字の範囲は0~F
2byteを16進数の1byteに変換する
それが3組できて、それぞれの対応した1byteをred,green,blueに代入する
そして試しにcolor-0だけいじるようにしたpatchがこれ

diff -Narup fbterm-1.7.orig/src/fbshell.cpp fbterm-1.7/src/fbshell.cpp
--- fbterm-1.7.orig/src/fbshell.cpp 2014-03-19 20:26:38.615617000 +0900
+++ fbterm-1.7/src/fbshell.cpp  2014-03-19 20:27:41.665617000 +0900
@@ -39,7 +39,7 @@
 #define screen (Screen::instance())
 #define manager (FbShellManager::instance())
 
-static const Color defaultPalette[NR_COLORS] = {
+static Color defaultPalette[NR_COLORS] = {
    {0x00, 0x00, 0x00}, /* 0 */
    {0xaa, 0x00, 0x00}, /* 1 */
    {0x00, 0xaa, 0x00}, /* 2 */
@@ -552,6 +552,31 @@ static s32 tty0_fd = -1;
 
 void FbShell::switchVt(bool enter, FbShell *peer)
 {
+    s8 color[7]={0}, rgb[3]={0};
+    u32 i,j,x;
+    Config::instance()->getOption("color-0", color, sizeof(color));
+    for(i=0;i<3;i++){
+        for(j=0;j<2;j++){
+            x=i*2+j;
+            if(('0' <= color[x]) && (color[x] <= '9'))
+                rgb[i]|=(color[x]-48);
+            else if(('A' <= color[x]) && (color[x] <= 'F'))
+                rgb[i]|=(color[x]-55);
+            else if(('a' <= color[x]) && (color[x] <= 'f'))
+                rgb[i]|=(color[x]-87);
+            else
+                goto NoTouch;
+            if(!j)
+                rgb[i]<<=4;
+        }
+        if(i==2){
+            defaultPalette[0].red=rgb[0];
+            defaultPalette[0].green=rgb[1];
+            defaultPalette[0].blue=rgb[2];
+        }
+    }
+NoTouch:
+
    if (tty0_fd == -1) tty0_fd = open("/dev/tty0", O_RDWR);
    if (tty0_fd != -1) {
        seteuid(0);

後はcolor-15までループさせればいいだけなのでループさせてから
処理をオブジェクト化して終了

後書き

backgroundとforegroundの色を独立して書くのは何かもう面倒臭いからまたやりたくなったら書きます

これで使いやすいブラウザがあれば最強なんだが
w3mは最高に使いにくい
vimperatorライクで使えたら最高なんだがな…