Windows Subsystem for Linuxのインストールからmikutterをそれらしく動かすまで

眼鏡 (@calkinos) | Twitter氏に便利ツールを教えてもらったので書こうと思いました。

こちらのツールを使うと好きなrootfsでWSL環境を生やせるんですね。

というわけでUbuntu on Windows同梱のinstall.tar.gzを使って、普段使いの環境とは別のUbuntuを作り、そこにmikutterを入れてみたので、メモがてら一連の流れを書いておきます。何番煎じだって? きっとなにかの やくにたつから。

なお、バトル要素はありません。

〈2018-04-10 追記〉 mozcを使う場合の記事を書きました。 WSLでuim-mozcを使う - cobodoのブログ

Ubuntu on Windowsのインストール

まずWindows 10でLinuxプログラムを利用可能にするWSL(Windows Subsystem for Linux)をインストールする:Tech TIPS - @ITなどを参考にしてWSLを有効化し、Ubuntuを入れましょう。Ubuntuじゃなくてもいいです。

GitHub - yuk7/WSL-DistroLauncher: WSL Distribution Launcher clone. (Win10 FCU 64bit or later.)にArchなどのrootfsが置いてあるようなのでそちらを使ってもよいはずです。

Linuxとかは全然わかりませんし、この記事ではUbuntuを前提にします。

インストールしたら、まず何はともあれアップデートしましょう。横着なのでいつも↓のようなことをします。

$ for i in update full-upgrade autoremove autoclean; do sudo apt $i -y; done

また、日本語言語パックを入れ、ロケールを設定しておきます。

$ sudo apt install language-pack-ja
$ export LANG=ja_JP.UTF-8
$ echo 'export LANG=ja_JP.UTF-8' >> .bash_profile

あとは必要だと思ったものを適当にaptで入れましょう。gitは既に入っているようです。

(オプション)rbenvのインストール

現在配布されているUbuntu on Windowsは16.04.4 LTSなので、aptでrubyを入れると2.3.0です。最新のmikutterは動作環境をRuby 2.3.0 以降としていますから、問題はないはずです。新しいrubyを使いたい人はrbenvを入れましょう。

ググれば使い方が山ほど出てきますから、ここでは詳しく説明しませんが、Ubuntuの場合、先にbuild-essentialパッケージをインストールする必要があります。このパッケージは、gccなどのコンパイラや、makeなどのビルドツール、それに基本的なライブラリをセットでインストールするメタパッケージです。

$ sudo apt install build-essential

パッケージ数が多いので、多少時間がかかります。

準備できたら、rbenvのインストールに入ります。

$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
$ cd ~/.rbenv && src/configure && make -C src
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
$ export PATH="$HOME/.rbenv/bin:$PATH"
$ ~/.rbenv/bin/rbenv init
$ eval "$(rbenv init -)"
$ mkdir -p "$(rbenv root)"/plugins
$ git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build

これでrbenvがインストールされました。~/.bashrc にもeval "$(rbenv init -)"を書いておくと便利だと思います。

次のコマンドを実行すると、正しくインストールされているかチェックしてくれます。

$ curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-doctor | bash

次にrbenvを使ってrubyをビルドしましょう。現在最新の安定版は2.5.1なので、これを入れてみます。が、実はライブラリが足りていないのであらかじめ入れてからビルドしましょう。

$ sudo apt-get install -y libssl-dev libreadline-dev zlib1g-dev
$ rbenv install 2.5.1

この、足りないライブラリをインストールするコマンドはrbenvがビルドに失敗した時に教えてくれます。便利ですが、失敗するまでにも結構時間がかかるので、先に書いておきました。

インストールが済んだら、とりあえずそれを使うようにしておきましょう。

$ rbenv global 2.5.1
$ ruby --version
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]

ついでに、bundlerをインストールしておきます。

$ gem install bundler

mikutterのインストール

本体です。アップデートが楽なようにgit cloneしてしまいましょう。

まず必要なライブラリを入れて*1から、依存gemも入れてしまいます。

$ git clone git://toshia.dip.jp/mikutter.git ~/mikutter
$ cd ~/mikutter
$ git checkout 3.6.5
$ sudo apt install libglib2.0-dev libidn11-dev
$ bundle install --path=vendor/bundle

途中、足りないライブラリがあると、bundlerが自動的にsudo apt-get installしようとするようで、パスワードを求められる場合があります。

ここまででmikutterは動きます。動くはずです。実際には、素の状態だと以下のようなエラーメッセージを吐いて終了します。

$ ruby ./mikutter.rb
Traceback (most recent call last):
        38: from ./mikutter.rb:42:in `<main>'
        37: from /home/cobodo/mikutter/core/miquire.rb:18:in `miquire'
        36: from /home/cobodo/mikutter/core/miquire.rb:75:in `miquire'
        35: from /home/cobodo/mikutter/core/miquire.rb:75:in `each'
        34: from /home/cobodo/mikutter/core/miquire.rb:76:in `block in miquire'
        33: from /home/cobodo/mikutter/core/miquire.rb:95:in `file_or_directory_require'
        32: from /home/cobodo/mikutter/core/miquire.rb:98:in `miquire_original_require'
        31: from /home/cobodo/mikutter/core/miquire.rb:98:in `require'
        30: from /home/cobodo/mikutter/core/boot/load_plugin.rb:10:in `<top (required)>'
        29: from /home/cobodo/mikutter/core/miquire_plugin.rb:96:in `load_all'
        28: from /home/cobodo/mikutter/core/miquire_plugin.rb:36:in `each_spec'
        27: from /home/cobodo/mikutter/core/miquire_plugin.rb:33:in `each'
        26: from /home/cobodo/mikutter/core/miquire_plugin.rb:33:in `each'
        25: from /home/cobodo/mikutter/core/miquire_plugin.rb:38:in `block in each_spec'
        24: from /home/cobodo/mikutter/core/miquire_plugin.rb:98:in `block in load_all'
        23: from /home/cobodo/mikutter/core/miquire_plugin.rb:146:in `load'
        22: from /home/cobodo/mikutter/core/utils.rb:278:in `atomic'
        21: from /home/cobodo/.rbenv/versions/2.5.1/lib/ruby/2.5.0/monitor.rb:226:in `mon_synchronize'
        20: from /home/cobodo/mikutter/core/utils.rb:278:in `block in atomic'
        19: from /home/cobodo/mikutter/core/miquire_plugin.rb:147:in `block in load'
        18: from /home/cobodo/mikutter/core/miquire_plugin.rb:147:in `each'
        17: from /home/cobodo/mikutter/core/miquire_plugin.rb:149:in `block (2 levels) in load'
        16: from /home/cobodo/mikutter/core/miquire_plugin.rb:146:in `load'
        15: from /home/cobodo/mikutter/core/utils.rb:278:in `atomic'
        14: from /home/cobodo/.rbenv/versions/2.5.1/lib/ruby/2.5.0/monitor.rb:226:in `mon_synchronize'
        13: from /home/cobodo/mikutter/core/utils.rb:278:in `block in atomic'
        12: from /home/cobodo/mikutter/core/miquire_plugin.rb:147:in `block in load'
        11: from /home/cobodo/mikutter/core/miquire_plugin.rb:147:in `each'
        10: from /home/cobodo/mikutter/core/miquire_plugin.rb:149:in `block (2 levels) in load'
         9: from /home/cobodo/mikutter/core/miquire_plugin.rb:146:in `load'
         8: from /home/cobodo/mikutter/core/utils.rb:278:in `atomic'
         7: from /home/cobodo/.rbenv/versions/2.5.1/lib/ruby/2.5.0/monitor.rb:226:in `mon_synchronize'
         6: from /home/cobodo/mikutter/core/utils.rb:278:in `block in atomic'
         5: from /home/cobodo/mikutter/core/miquire_plugin.rb:158:in `block in load'
         4: from /home/cobodo/mikutter/core/miquire_plugin.rb:158:in `load'
         3: from /home/cobodo/mikutter/core/plugin/gtk/gtk.rb:5:in `<top (required)>'
         2: from /home/cobodo/mikutter/core/plugin/gtk/gtk.rb:5:in `require'
         1: from /home/cobodo/mikutter/vendor/bundle/ruby/2.5.0/gems/gtk2-3.2.0/lib/gtk2.rb:13:in `<top (required)>'
/home/cobodo/mikutter/vendor/bundle/ruby/2.5.0/gems/gtk2-3.2.0/lib/gtk2.rb:13:in `init': Cannot open display:  (Gtk::InitError)

それもそのはずで、WSLには画面がなく、従ってGUIも無いのです。

guiプラグインを抜いた状態でmikutterを動かす話はmikutterの薄い本などにしばしば登場しますし、そういった用途であればここまででOKでしょう。

そうではなく、mikutterをGUIで楽しむには、いくつかやらなければならないことがあります。

Windows側へX Windowサーバをインストール

LinuxGUI(の実装のひとつ)はX Window Systemというもので、コレは各GUIアプリケーションがソケット経由で描画情報を送ってサーバ側に描画させる仕組みを取っています。

そこで、Windows側にXサーバを立て、WSL側からそこに描画情報をソケット通信で送れば、Windows上の画面にウィンドウを描画することができるわけです。

Windows上で動くXサーバの実装はいくつかありますが、ここではVcXsrvを使います。

https://sourceforge.net/projects/vcxsrv/

VcXsrvはウィンドウのアイコンをWindowsのタスクバー上でちゃんと出してくれるので好きです。普通にインストールして起動するだけでいいはずです。ファイアウォールの警告が出ると思いますので適当な範囲で許可しましょう。

WSL側で、環境変数DISPLAYに、使用するXサーバを指定して起動してやれば、すぐに表示できます。

$ export DISPLAY=:0.0
$ echo 'export DISPLAY=:0.0' >> ~/.bash_profile
$ ruby ./mikutter.rb

ウィンドウが表示されたと思いますが、文字化けしまくっていると思います。

フォントのインストール

文字化けしているのは日本語フォントが入っていないからです。適当に入れましょう。

$ sudo apt install fonts-noto fonts-noto-cjk fonts-noto-mono ttf-ancient-fonts
$ ruby ./mikutter.rb

今度は文字化けせずに表示されたのではないでしょうか。

他のフォントを使いたい場合は、適当にaptから入れるか、~/.local/share/fontsディレクトリの中に.ttfや.otfのファイルを置いて、

$ sudo fc-cache -fv

しましょう。

ブラウザの起動

mikutterからtwitterの認証を行うにはブラウザの起動が必要なので、設定しましょう。

最近のWSLはWindowsアプリケーションを直接実行できます。つまり、Windows側のブラウザをWSLから起動できるということです。この時、URLを引数に渡せばそのURLが普通に開きます。

mikutterでは、「設定>表示>URLを開く方法」で「次のコマンドを使う」を選択し、WSL側から見たブラウザのexeファイルへのパスを指定すれば、Windows側のブラウザに渡せます。私はFirefox Developer Editionを使用しているので、この場合は/mnt/c/Program Files/Firefox Developer Edition/firefox.exeとなります。

もちろんWSL内部で普通にfirefoxなどをインストールし、mikutterと同様の方法でWindows側のXサーバに描画させてもよいですが、通常はWindows側のブラウザを使った方が便利でしょう。

uim+anthyのインストール

〈2018-04-10 追記〉 mozcを使う場合の記事を書きました。 WSLでuim-mozcを使う - cobodoのブログ 環境変数は以下のものを前提にしているので、mozcを使う場合でもこのまま進めてください。おそらくuimパッケージはインストールする必要があると思います。

次は日本語を入力できるようにしましょう。

Linuxにおける多くの日本語入力システムはプロセス間通信にdbusを使用しています。dbusは基本的にUNIXドメインソケットで通信を行いますが、WSLはまだUNIXドメインソケットに対応していません*2

そこでdbusを使わないものが必要になるわけですが、uimuim-anthyを使えばいいらしいです。インストールしましょう。kasumiはユーザー辞書編集用です。

$ sudo apt install uim uim-anthy kasumi
$ cat >> ~/.bash_profile <<EOL
IM_MODULE=uim
export XMODIFIERS=@im=\$IM_MODULE
export GTK_IM_MODULE=\$IM_MODULE
export QT_IM_MODULE=\$IM_MODULE
EOL
$ IM_MODULE=uim
$ export XMODIFIERS=@im=$IM_MODULE
$ export GTK_IM_MODULE=$IM_MODULE
$ export QT_IM_MODULE=$IM_MODULE

これで、mikutter上で日本語入力ができるようになりました*3

ここまでで、それなりに普通に、mikutterが使えるのではないでしょうか。好きなプラグインを入れましょう。

(オプション)pulseaudioのインストール

イベントが発生した時にみくったーちゃんにしゃべってもらいたいあなたは、更にやることがあります。

WSLにはサウンドデバイスがありません。従ってALSAは動きません。ですから音は鳴りません。

そこでX Windowと同じく、ソケット通信を使います。

WSL内でクライアント用のpulseaudioデーモンを動かし、Windows側でサーバ用のpulseaudioデーモンを動かすのです。すると、WSL側のpulseaudioデーモンが送信した音声データを、Windows側のpulseaudioデーモンがWindowsのサウンドデバイスで再生してくれるのです。

まずWSL内でpulseaudioをインストールしましょう。

$ sudo apt install pulseaudio

次にpulseaudioの設定をします。/etc/pulse/client.confの22行目にある

; default-server =

という行を、次のように書き換えます。

default-server = tcp:localhost

続いて/etc/pulse/default.paテキストエディタで開き、86行目にある

#load-module module-native-protocol-tcp

の先頭の#を消し

load-module module-native-protocol-tcp

とします。

次はWindows側です。www.freedesktop.org内にあるPulseAudioのWindowsサポートに関するページに、

For convenience, a zipfile containing preview binaries is available.

という文とリンクがあると思います。これをダウンロードするとpulseaudio-1.1.zipが落ちてきます。適当なディレクトリに展開します。

展開したファイルのうち、etc/pulse/default.paテキストエディタで開き、61行目の

#load-module module-native-protocol-tcp

load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1 auth-anonymous=1

と書き換えます。

コマンドプロンプトを開いて、展開したディレクトリのbin/cdし、pulseaudio.exeを実行します。すると待機状態になります。ここでもファイアウォール警告が出ると思います。適当にやりましょう。

ここで、WSL側で

$ paplay ~/mikutter/core/skin/data/sounds/mikutter-se.wav

を実行すると、「み↓くった~↑♪」としゃべってくれることでしょう。

このpulseaudio.exeは、-dを付けてデーモンモードで起動したりしても、時間経過(もしくは何らかの条件)で勝手に終了してしまうため、WSL側かMSYS2などを使ってコンソールを開きっぱなしにした上で、以下のようなスクリプトで常駐させてやるとよいでしょう。

#!/bin/bash
cd /path/to/pulseaudio-1.1/bin
while ./pulseaudio.exe || true; do
    echo
    echo '!!!pulseaudio restart!!!'
    echo
done

次にmikutterから音を鳴らす方法です。mikutterはデフォルトではaplayコマンドを使ってALSA経由で音を鳴らしますが、これは前述の通り鳴りません。また、GitHub - toshia/mikutter-pulseaudio: mikutterの効果音をPulseAudioを通して再生するというプラグインもありますが、こちらが利用するpacmdコマンド経由の再生は、WSL環境だと何故か効かないようです。

そこで、標準のaplayを使って再生するプラグインを、paplayを使って再生するように改造したものがGitHub - cobodo/mikutter-paplayです。これを使えば、ふぁぼられた時などにmikutterに任意のサウンドを再生させることができます。

使い方は、インストールして、設定>サウンドで選択するだけです。

$ mkdir -p ~/.mikutter/plugin && git clone git://github.com/cobodo/mikutter-paplay ~/.mikutter/plugin/paplay

(オプション)Windows側へBurntToastをインストール

mikutterには通知がある場合にポップアップを出してお知らせする機能がありますが、これはLinuxnotify-sendコマンドを経由しています。これはlibnotify-binパッケージを入れれば実行できますが、実行しても何も表示されません。

そこで、PowerShell経由でWindowsの通知機能を使えるBurntToastというツールをインストールし、WSL側からpowershell.exeを叩いて通知を出すことにしたのがGitHub - cobodo/mikutter-burnt-toastというプラグインです。

まずPowerShellを管理者権限で開き、以下のコマンドを実行してBurntToastをインストールします。

> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
> Install-Module -Name BurntToast

次にこのプラグインをmikutterにインストールします。

$ mkdir -p ~/.mikutter/plugin && git clone git://github.com/cobodo/mikutter-burnt-toast ~/.mikutter/plugin/burnt_toast

設定>BurntToastから、Windows側から見たWSLのrootfsへのパスを設定してください。筆者の環境では以下のような値でした。

C:\Users\cobodo\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\rootfs

これで通知が表示されるようになりますし、履歴も見られます。

f:id:cobodo:20180331033001p:plain

このようにアイコン付きで表示されます。

おわりに

WSLはまだまだ発展途上で、いろいろな機能が足りませんが、ソケット通信とWindows側のexe起動があればどうとでもなることがわかってもらえたと思います。

日本語入力に関しては正直なところ微妙ですが、使えないほどではありません。mozcが動けばいいんですが……

この記事を読んでWSL上でmikutterを楽しむ人が増えれば幸いです。

*1:libidn11-devは、これに依存しているgemがbundlerによる自動インストールに対応しておらず、どのパッケージを入れればいいのか自明ではないので注意。

*2:将来的に対応する予定はあるらしいです。

*3:mikutterはGTKで動くのでQT_IM_MODULEなどは必要ないと思いますが、もののついでです。