skk is … #2

絵文字の変換を行うために
https://github.com/uasi/skk-emoji-jisyo
の SKK 辞書を使いたい。ダウンロードして、dictionary_list に追記する。

file=$FCITX_CONFIG_DIR/SKK-JISYO.emoji.utf8,mode=readonly,encoding=UTF-8,type=file

が、認識してくれない。なんで…? と思ってソースを見てみたら
https://gitlab.com/fcitx/fcitx-skk/blob/master/src/skk.c#L196
なるほど readonly の場合は $FCITX_CONFIG_DIR の展開をしていない。というか、この関数自体の文字列処理とメモリ管理の書き方がなんとなく危うい感じがする…。

そういうわけで仕方ないのでベタに書いて認識。

file=/home/akahuku/devel/ref/skk/skk-emoji-jisyo/SKK-JISYO.emoji.utf8,mode=readonly,encoding=UTF-8,type=file

その他の環境変数や “~” はどうなんだろうか。このパス名は libskk を経由して GLib の File_for_path() という立派な関数に渡されるのでそこでなんかうまいことしてくれるのかもしれない。試してない。

しかし認識したのはよいものの、入力中に “/” で入る見出し語をアルファベットで編集するモードでこの辞書内の見出しを補完できない。

うーむ。まあこの補完って先頭一致なのでできたとしてもあんまり嬉しくはないのだけど。例えば heart だけでも

black_heart
blue_heart
broken_heart
couple_with_heart
couple_with_heart_man_man
couple_with_heart_woman_man
couple_with_heart_woman_woman
gift_heart
green_heart
heart
heart_decoration
heart_eyes
heart_eyes_cat
heartbeat
heartpulse
hearts
heavy_heart_exclamation
kissing_heart
purple_heart
revolving_hearts
sparkling_heart
two_hearts
yellow_heart

こんな感じで先頭だったり真ん中だったり末尾だったり単語中にあったりごちゃごちゃなので。

skk is …

mecab-skkserv のテスト中、キー押下から反応が返ってくるまでやたら時間がかかる現象に陥ったことが一度あると前の記事に書いた。

調べてみると、この原因はまさに mecab-skkserv だった。発生する条件は、見出し語の編集中に Ctrl+I もしくは Tab を押す、つまり補完を開始するとそういう状態になる。

この補完機能は skkserv のプロトコルで言うと 4 に当たるのだけど、mecab-skkserv.cpp のメインループでそれに対応する応答を行っていないため、fcitx 側(正確には libskk)が応答を延々待ち続けることになるのだろう。知らないコマンドを無視する mecab-skkserv の問題とも言えるし、レスポンスを読む際にタイムアウトを考慮しない呼び出し側の問題とも言える。

--- mecab-skkserv-original.cpp	2020-01-17 17:37:19.897237779 +0900
+++ mecab-skkserv.cpp	2020-01-17 17:38:26.822432515 +0900
@@ -33,20 +33,10 @@
 #include <stdexcept>
 #include <set>
 
-#ifdef HAVE_GETOPT_H
-#include <getopt.h>
-#endif
-
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 
-#if defined HAVE_GETOPT_H && defined HAVE_GETOPT_LONG
-#include <getopt.h>
-#else
-#include "getopt.h"
-#endif
-
 #define STDIN (fileno(stdin))
 #define BUFSIZE 8192
 #define DEFAULT_CAND_SIZE 20
@@ -155,6 +145,10 @@
       case '3':
 	std::cout << "localhost:127.0.0.1: " << std::flush;
 	break;
+       
+      case '4':
+	std::cout << '4' << std::endl;
+	break;
       }
     }
 

というわけで修正自体は数行付け足すだけでいいのだけど、このパッチを誰にどう投げればいいのかよく分からない。助けて偉い人。

mozc is … #4

ということで mecab-skkserv をビルドする。

mecab 本体のビルド

まず mecab 本体を落としてきて普通にビルドする。特に記すことはない。このページで提供されている辞書は mecab-skkserv には不要なので作っても作らなくてもいい。
https://taku910.github.io/mecab/

mecab-skkserv のビルド

http://chasen.org/~taku/software/mecab-skkserv/
おそらく幾つかの点でコンパイルエラーになるので

  • mecab-skkserv.cpp で getopt.h を #include している箇所を削除
  • dicrc に cost-factor = 700 とかを追記する(値は700〜800で任意とのこと)

等々を施す必要があるだろう。ちなみにビルドの際に辞書も構築されるが、特に ./configure で指定しなければ euc-jp エンコーディングになる。

systemd ユニットの作成

mecab-skkserv 本体のインストールまで済んだら次は xinetd か tcpserver に登録することになっているが、2020年の現在はそれらではなく systemd の話になる。以下のファイルを新規作成する。

/etc/systemd/system/mecab-skkserv.socket ファイル

[Unit]
Description=mecab-skkserv socket

[Socket]
ListenStream=1178
Accept=yes

[Install]
WantedBy=sockets.target

/etc/systemd/system/mecab-skkserv@.service ファイル

[Unit]
Description=mecab-skkserv service
Requires=mecab-skkserv.socket

[Service]
Type=simple
ExecStart=/usr/local/bin/mecab-skkserv
StandardInput=socket
StandardError=journal

[Install]
WantedBy=multi-user.target

systemd に対して有効にする。この有効化はマシン再起動をまたいでも永続的である。

$ systemctl enable mecab-skkserv.socket
$ systemctl start mecab-skkserv.socket
$ systemctl status mecab-skkserv.socket

この設定だと外のマシンからの接続も受け付けるので、拒否する必要があるなら iptables とかのレベルでうまいことする。

この状態でちゃんと動いてるかどうかは例えば

$ echo "2 " | nc localhost 1178

などとして確認できる。mecab-skkserv のバージョン情報が返ってくればとりあえず接続の受付と起動は正常。

fcitx-skk に対して mecab-skkserv を辞書として登録する

~/.config/fcitx/skk/dictionary_list に以下の行を追加

type=server,host=localhost,port=1178

このファイルでは辞書の指定の順番が重要な気がするが、SKK_JISYO.* の上に置く場合と下に置く場合でどう違うのかは試してない。

また、上記の通り普通に mecab-skkserv をビルドすると euc-jp エンコーディングの辞書が構築される。skkserv プロトコル自体が euc-jp で動くことになっているのでそれで正しいのだが、絵文字など unicode でしか表現できない文字を辞書に含めることができなくなる。その場合辞書を utf-8 で構築し、dictionary_list には

type=server,host=localhost,port=1178,encoding=UTF-8

などと指定すれば良い。

あとは fcitx を再起動すれば普通に使えるようになる。素の SKK だと Watashi<spc>noNamae<spc>haNakano<spc>desu. などと打っていたものが Watashino<spc>Namaeha<spc>Nakanodesu. くらいまでシンプルになる。いやこれはあんまり良い例じゃないな。とにかく送り仮名を気にする必要がほとんどの場合なくなる。

困った点としては

  • skkserv の向う側の辞書は一切変換の学習をしない。学習を skkserv 辞書にフィードバックする仕様自体がプロトコルにない?
  • たまにキー押下からレスポンスが返ってくるまで数十秒かかる状態に陥ることがある。たまにというか今まで1回だけそうなった。原因不明
  • 素の SKK に比べると、変換のレスポンスはほんのわずかに遅れる感じはする。ほんの0.1〜0.2秒ほど。慣れの範囲だろう

とはいえ普通に快適なので、当分 mozc を使うことはないだろう。

mozc is … #3

もう少し SKK で遊んでみよう。SKK といえば辞書をネットワーク越しに持てるのが特徴のひとつである。そのへんを追いかけてみる。skkserv なんて15年くらい前の話題なので2周くらい遅れてるわけだが気にしてはいけない。

さてコンピュータ上で日本語を入力する現代的かつ一般的な方式というのは、文字入力できるフィールドにおける挿入ポイントに対してフロントエンドとなるインターフェースが用意されていて、そのプリエディットバッファ内での入力と文節単位の(漢字)変換結果の選択をおこなったのち、最終的な変換結果全体をフィールドに送出するというものだ。

しかし、この方式が本当に日本語入力の完成された到達点なのか? と言うとそうは思えない。なんか日本語変換してるときって言い様のないストレスを感じません? この原因はプリエディットというものの存在にあると思う。正確に言えばプリエディットという非常にモーダル性の高いインターフェースに役割を持たせすぎて、そのライフタイムを長くさせる設計が良くない。

思うに、プリエディットがアクティブで、そこにいろんなものが溜まってる状態というのは、いわば便秘に苛まれている状態そのものなんだろう。その状態のストレスがどんなものか、説明するまでもない。食べたものも入力したものも体内での役目を終えたら即出力されるのがストレスのない生き方なのは自明の理なのに、プリエディットに頼りすぎるという全く正反対の食生活を押し付けてくる現代の日本語入力インターフェースは健康的にも邪悪というより他はない。

そういう観点で SKK を見てみると、SKK の本質からは若干ずれている気がするが、漢字変換の必要がない文字種についてはプリエディットを経ずに直接入力できるという仕様が、SKK を通して入力する際の妙な気楽さ、ストレスの低さに貢献している要素のひとつであるように思える。これは非常に重要。

一方で、SKK の本質である漢字変換の際に送り仮名の開始位置を明示しなければならない仕様は、それが手書きに通じて良いと肯定する意見もあるが、やはりさすがに使いやすさをスポイルしている要素だと言わざるを得ないと思う。ここがなんとかなるとかなり嬉しい。

そういうことで最初に戻るのだが、SKK の場合辞書の部分が分離されていて後から追加できたり、見出し語から対応する語を返す仕様さえ満たしていれば他はなんでもありだったりするので、それを利用してもう少し楽に変換したい。具体的には mecab-skkserv を入れたい。mecab-skkserv はその名の通り mecab をバックエンドに持つ辞書で、これにより送り仮名を意識することなく変換することができるはずだ。ただし、mecab-skkserv の説明で「疑似的に連文節変換ができる」と言われることがあるが、SKK にも mecab-skkserv にも連文節を編集する機能はないのでそれはあまり正しくはない。素の SKK が漢字列とそれに付随する送り仮名の頭という風変わりな単位で変換するのに比較してより自然に文節単位で変換できるようにはなる。

mozc is … #2

日本語を入力するために mozc を使用しているわけなのだが、これが何だかよく分からないが微妙にヘンなのがすごく気になる。たとえば「〜方が」とか「〜という奴」などと打っても、「方」や「奴」を絶対に変換してくれない。つまり、それらを文節の区切りの開始として扱ってくれない。そして文節を指定しなおしてもそれを絶対に学習してくれない。どうしてなの。ハードコーディングされてるの?

そういうわけなので SKK に乗り換えてしまった。

基本的に特に設定を変えずに使ってるが、ローマ字変換テーブルだけちょっといじった。

$ mkdir -p ~/.config/libskk/rules/default-ex
$ cp -R /usr/share/libskk/rules/default/* ~/.config/libskk/rules/default-ex/
$ vim ~/.config/libskk/rules/default-ex/metadata.json
$ vim ~/.config/libskk/rules/rom-kana/default.json
--- /usr/share/libskk/rules/default/rom-kana/default.json	2018-04-03 21:32:28.000000000 +0900
+++ /home/akahuku/.config/libskk/rules/default-ex/rom-kana/default.json	2020-01-15 02:37:30.609068213 +0900
@@ -178,11 +178,11 @@
             "tyu": ["", "ちゅ" ],
             "u": ["", "う" ],
             "vv": ["v", "っ" ],
-            "va": ["", "う゛ぁ" ],
-            "ve": ["", "う゛ぇ" ],
-            "vi": ["", "う゛ぃ" ],
-            "vo": ["", "う゛ぉ" ],
-            "vu": ["", "う゛" ],
+            "va": ["", "ゔぁ" ],
+            "ve": ["", "ゔぇ" ],
+            "vi": ["", "ゔぃ" ],
+            "vo": ["", "ゔぉ" ],
+            "vu": ["", "ゔ" ],
             "ww": ["w", "っ" ],
             "wa": ["", "わ" ],
             "we": ["", "うぇ" ],
@@ -211,6 +211,7 @@
             "yo": ["", "よ" ],
             "yu": ["", "ゆ" ],
             "zz": ["z", "っ" ],
+            "z ": ["", " " ],
             "z,": ["", "‥" ],
             "z-": ["", "〜" ],
             "z.": ["", "…" ],
@@ -235,8 +236,12 @@
             ":": ["", ":" ],
             ";": ["", ";" ],
             "?": ["", "?" ],
+            "!": ["", "!" ],
+            "~": ["", "〜" ],
             "[": ["", "「" ],
-            "]": ["", "」" ]
+            "]": ["", "」" ],
+            "(": ["", "(" ],
+            ")": ["", ")" ]
         }
     }
 }

あとは fcitx を再起動して SKK の設定から新しく作ったルールを選び直す。ただ、SKK の辞書マネージャが明らかにちゃんと動いてないっぽいのが気になる。

まあこの GUI は使わず、タイピング方式は ~/.config/fcitx/skk/rule を、辞書のリストは ~/.config/fcitx/skk/dictionary_list を直接編集すればいいだろう。ちなみに fcitx に設定を再リロードさせるには fcitx-remote -r とすればいい。

I did nothing but

今日日のソフトウェアは自動的にアップデートが行われるものが少なくないので、「何もしてないのに壊れた」ということが結構起こる。数日前から gimp が起動しなくなってしまった。

dmesg を見てみると、何やら AppArmor のエラーのようだ。また、snapcraft のフォーラムにも同じような話題が投稿されている。しかし解決には至ってないようだ。

$ snap list gimp --all
Name  Version  Rev  Tracking  Publisher     Notes
gimp  2.10.12  189  stable    snapcrafters  disabled
gimp  2.10.12  227  stable    snapcrafters  -

$ snap info gimp
name:      gimp
summary:   GNU Image Manipulation Program
publisher: Snapcrafters
contact:   https://github.com/snapcrafters/gimp/issues
license:   unset
description: |
  Whether you are a graphic designer, photographer, illustrator, or scientist, GIMP provides you
  with sophisticated tools to get your job done. You can further enhance your productivity with GIMP
  thanks to many customization options and 3rd party plugins.
  
  This snap is maintained by the Snapcrafters community, and is not necessarily endorsed or
  officially maintained by the upstream developers.
commands:
  - gimp
snap-id:      KDHYbyuzZukmLhiogKiUksByRhXD2gYV
tracking:     stable
refresh-date: today at 03:52 JST
channels:
  stable:    2.10.12 2020-01-10 (227) 429MB -
  candidate: 2.10.14 2020-01-12 (243) 179MB -
  beta:      ↑                              
  edge:      2.10.14 2020-01-10 (243) 179MB -
installed:   2.10.12            (189) 229MB -

なるほど確かに数日前に最新版になっているらしい。最新だけど動かない。ダメじゃん。というわけで

$ sudo snap revert gimp                                                                                                                                                                   gimp reverted to 2.10.12

とこんな感じでひとつ前に戻すと起動した。

become a historian

ふたばの画像掲示板で履歴機能が拡充されたので見てみる。

もともと、カタログの1モードとして過去にレスしたスレッドを集計する機能があり、それが履歴という扱いだった。内部的に考えれば、post したリモートホストが自分であるレスを含むスレッドを select するというサーバ側の SQL のクエリの話になる。

最近拡充されたのはそれとは別立てで、開いたスレッドの履歴を持っていてその一覧を表示するというものである。開いたスレッドの一覧データはどこにあるかと言うと、サーバ側ではなく、クライアントの cookie である。つまりカタログをサーバへリクエストする際にその cookie を投げると、サーバはそれに対応したスレッドの一覧を組み立てて返してくる。

これは良くない設計に思える。これに限らず、クライアント側はセッション ID だけを持たせてセッションにぶら下がるデータはサーバ側が持つほうが正道に思える箇所が色々あるのだが、長いことふたばはその方法を取らない。

それはそれとしてもう1つ不思議なのは、post した履歴と開いた履歴は分ける必要なくない? ということだ。常に post した履歴は開いた履歴の部分集合であるはずだ。これは誰でもそう思う疑問のはずで、まあまだ色々と作りかけなのかもしれない。

そういうわけなのでとりあえず見た履歴の機能をベースにして、post したスレッドを先頭に寄せるという加工を施すようにした。見た履歴を追加したり削除したりする機能はまだ作ってない。

背景色が付いている方が見たスレッド、かつ post したスレッド。そうではない方は見ただけのスレッド

introduce the passive tracking

スレッドの自動追尾というものを先日作った。これはユーザの指示によってスレッドのオートリロードをオンオフするものであり、かつスレッドの勢いに応じたリロード頻度を用いるので、いわばアクティブトラッキングと言える。作った際の感想としては例えば実況スレとかよりもむしろ寝落ちしてしまった際に有効かもしれない…というのは以前書いた。ただし寝落ちするくらいフニャフニャな状態だと、自動追尾を事前にオンにしていない場合がほとんどである。これをどうするか。

というわけで今回はそれと対になるパッシブトラッキングというものを作った。これはスレッドの消える時刻を算出した際、その消える時刻と現在の時刻との中間地点で1回タイマーを仕掛け、そこで続きを読むものだ。例えばスレッドの寿命が最低1時間保証されている板でスレッドを立てた場合、30分後に自動的に続きを読む。その次は15分後、その次は7分30秒後…という調子だ。スレッドの寿命が尽きるまで追いかける機能はそのままで、アクティブトラッキングに対して続きを読む頻度は相当抑えられる。

パッシブトラッキングは常に有効で、どのスレッドでも開いた時点から動作する。ただしアクティブトラッキングとの併存はできず、アクティブ側が優先される。

Link to anything #2

めったにないことではあるが、塩辛瓶やあぷ小のファイル名をコメントに含める際、拡張子を省略する人がいる。その場合の自動リンクはどうあるべきか。

その場合、赤福プラスはいずれのアップローダに対してもファイル名の補完を試みるようになっている。塩辛瓶の場合は、補完のための JSONP インターフェースが用意されているのでそれを使う。誰が作ってくれたのか知らないけど、ありがたいことです。

さて、あぷとあぷ小のサムネイルサービスの場合はと言うとこちらも似たようなインターフェースを用意してある。https://appsweets.net/thumbnail/up2/fu99999s.js などと呼び出すと、拡張子は js だが、そのファイルの情報が json で返ってくる。なぜ js かと言えばそれによって CloudFlare のキャッシュに乗るからである。ちなみに Access-Control-Allow-Origin: * としているので cors の制限は受けない。普通に xhr や fetch で読み出せばいい。

{
  "content": "data:image/png;base64,...",
  "board":"up2",
  "name":"fu25465.png",
  "base":"fu25465",
  "mime":"image/png",
  "size":9152,
  "comment":"",
  "dimensions":{
    "thumbnail":{
      "width":188,
      "height":250
    },
    "original":{
      "width":300,
      "height":400
    }
  }
}

こんな感じで返ってくる。content はサムネイル画像を data URI にエンコードしたものだ。

Link to anything

コメント中の自動リンク処理を整理し、100件弱くらいのテストケースを書き、それにパスするようにした。また、いくつかの既知のサイトに関して override scheme/preferred schemeの機構を設けた。前者はつまり、youtube とかは http://〜 と書かれていても https でリンクされる。逆に塩辛瓶は https://〜 と書いても http でリンクされる。後者はスキームが省略された時に選択されるデフォルトのスキームであり、ふたば内のリンクに https が適用される。