Windows Subsystem for Linux + X Windowを1.024倍くらい使いこなすための方法


 諸々の理由によりWindowsな皆様こんにちは。Bash on Ubuntu on Windows使ってますか?私はBash on openSUSE on Windows派です。

 Windows10の10月17日予定のアップデートFall Creators Update(1709)からBash on WindowsことWSLことWindows Subsystem for Linuxが正式版になり、あわせてUbuntuだけでなくopenSUSE/SLES、また予定ではFedoraなどディストリビューションを選ぶこともできるようになり、たいへん導入しやすくなりました。

 さて、このWSLは単体でもコンソールとしては優秀ですが、X Window Serverと組み合わせるとおおよそLinuxデスクトップっぽくなってたいへん素敵です。素敵度合いをアピールするために、これ見よがしなスクリーンショットを用意しておきました。


どれがLinuxアプリでどれがWindowsアプリかわからない…といいんですが。

 この記事はそんなWSL + X Windowをもう少しだけチューニングすることで、Linux GUI環境をストレスフリーに使うためのtweak記事です。

 WSLとX Window Serverの組み合わせ自体の方法については、すでに先達の方々がおられるので、そちらをご覧ください。(だいたい原理も何も知っているのでもう言わんでよかって人のためにopenSUSE on WSLでのLXDE導入方法も書いておきました。→ページ内ジャンプ

 なお、X Window ServerはVcXsrv、デスクトップ環境はLXDEで、スクリーンショットもそれに準じています。環境に応じて随時読み替えてください。ちなみに、Xmingは最新版(寄付版)を持っていないのであれば、VcXSrvの方がだいたいすべての面でいいです。

フォントの設定

 X Window Systemとフォントの関係はややこしいのですが、今どきはX Windowで表示されるフォントはLinux側のフォントです。Linux上のフォントは昔に比べればだいぶ綺麗になりましたが、Windowsアプリと一緒に使うと明らかな違和感があります。せっかくWindowsで使用しているので、Windowsフォントに合わせましょう。Windowsのフォントフォルダを丸ごとLinuxから見えるようにします。

# sudo ln -s /mnt/c/Windows/Fonts /usr/share/fonts/windows
# sudo fc-cache -fv

 これでWindows10フォントがLinuxから選べるようになったと思います。

 フォントが選べるようになったので、システムのルックアンドフィールをWindows10に合わせると、Linuxアプリの違和感がかなり軽減されます。Windows10のデフォルトは、Yu Gothic UIRegularでサイズは1011ぐらいだと思います。

高DPI環境への対応

 フォントの設定からわかるように、X WindowのフォントレンダリングはLinux側のフォントレンダリングです。Windows10は高解像度環境(HiDPI環境)でウィンドウを自動スケールしますが、Linuxから送られてきているのはレンダリング後の単なるビットマップなので、フォントが単に引き伸ばされてボケてしまいます。Windows側のスケーリングを無効化してLinux側でスケールしましょう。

VcXsrvのスケーリングを無効にする

 exeファイルのプロパティからスケーリングを無効にします。

Linuxでスケーリングする

 XmingやVcXsrvでは-dpiでスケーリングを起動時に指定することができます。~/.XresourcesファイルでXft.dpiパラメータで指定することもできるようです。Windows10の100%値は96のようです。ただし、微妙な差異により(Windowsのシステムフォントサイズが10.5になってるとか?)、計算通りにスケーリングしても、Windows10と合わなかったりするので、環境ごとに調整してみてください。

bash.exe画面を表示せずに任意のLinuxプログラムを起動する

 X Windowのアプリケーションを起動するという点においては、bashは不要です。できることならWindowsから直接的に必要なアプリケーションか、あるいはLXDEにおけるlxpanelのような、タスクバー(および設定メニュー込みのアプリケーションランチャー)プログラムを起動したいはずです。
 しかし、この記事を書いている時点では、bash抜きにWSLを起動する公式の手法はありません。WSLの起動は、bash.exeの実行とほぼ同義ですが、bash.exeは必ずコンソールを必要とし、bash.exeから起動されたバックグラウンドプロセスは、bash.exeを落とした時点で終了してしまいます。
 すると、たとえば「ファイル名を指定して実行」からbash -lc lxpanelなどと実行しても、こんな画面が残ってしまいます。

この画面を消します。

WSHスクリプト

 単純に考えると、lxpanelを実行した後のbash.exeの画面をhiddenにしてくれればいいのです。子プロセスの画面を隠してくれるフリーウェアも探せばありそうですが、Windows標準のWSHスクリプトで簡単に作れそうなので作ります。

run-linux.js
var shell = new ActiveXObject("WScript.Shell");
var args = WScript.Arguments;
var cmd = [];
var i;

for (i = 0; i < args.length; i++) {
  cmd.push(args(i))
}

shell.Run('bash -lc "' + cmd.join(' ') + '"', 0, true);
// bash -lc 'xxxx'をウィンドウなし(0)で、同期的(true)に実行します

// ※ここは最初、次のように書いていましたが、Windows10の何かの更新により、
// ''と""で解釈が変わるようになり、下のコードでは動かなくなったようです
// shell.Run("bash -lc '" + cmd.join(' ') + "'", 0, true);

 これで、X Window Serverが立ち上がってさえいれば、「ファイル名を指定して実行」からwscript run-linux.js xtermなどとすることで、あたかもbashを介在しなかったかのようにLinuxプログラムを起動することができます。また、Linuxプログラムが終了すると自動的にbash.exeも終了します。

XLaunchファイルの設定

 上記のWSHスクリプトは起動前にX Window Serverが立ち上がっていなければなりません。それはそれで面倒なので、同時に立ち上げる方法を考えます。
 単純にはスクリプトでVcXSrvを前もって起動するようにするのがよいのですが、実はXmingやVcXsrvの起動設定ファイル*.xlaunchファイルは、X Window Server起動後にローカルなプログラムを起動するように構成できます。本来何に使うのが正しいのかよくわからない(テスト用?)機能ですが、ここからWSHスクリプトを実行可能です。簡単のため、先ほどのWSHスクリプトファイルrun-linux.jsC:\\Program Files\VcXsrvに入れておきます。


Start a programを選んで

ここに入力すればいいのですが、厳しい文字数制限があって、入れることができません。
 なので、出来上がったxlaunchファイルをテキストエディタで次のように編集します。実態はXMLファイルなので、編集は容易です。LocalProgram属性がそれです。

run-with-wsh.xlaunch
<?xml version="1.0" encoding="UTF-8"?>
<XLaunch
  WindowMode="MultiWindow" ClientMode="StartProgram"
  LocalClient="True" Display="-1"
  LocalProgram="wscript run-linux.js lxpanel"
  RemoteProgram="" RemotePassword="" PrivateKey="" RemoteHost=""
  RemoteUser="" XDMCPHost="" XDMCPBroadcast="False" XDMCPIndirect="False"
  Clipboard="True" ClipboardPrimary="True" ExtraParams="-dpi 96" Wgl="True"
  DisableAC="False" XDMCPTerminate="False"
/>

 これでダブルクリックするだけで、いつでもlxpanelが立ち上がるようになりました。
ただし、このままではlxpanelやそこから起動するシェルのカレントディレクトリが/mnt/c/Program Files/VcXsrvになってしまいます。それが困る場合はLocalProgram="wscript run-linux.js cd; lxpanel"などとします。
またlxpanelはlxpanel --profile LXDEで起動したほうが、見た目がopenSUSEっぽくなります。お好みで。

ログアウト

 lxpanelのほうでも、ログアウトに備えておきます。ログアウト時にlxpanelctl exitを実行するようにします。この設定は、デスクトップ環境によっては不要なのかもしれません。これによりログアウトすると、無事にプロセスが死に、自動的にVcXsrvも落ちます。

※Windows 10 April 2018 Update(1803)から、ログアウトしてもデーモン化されたプロセスは生存できるようになったため、ログアウトしてもWSL環境が閉じられるとは限らないので注意してください。

dbusを設定するしない

※ここにはdbusの設定がいる風の説明がありましたが、時代遅れの真っ赤なウソでした。今のWSLではdbusは特に設定なしで動きます。お詫びして訂正いたします。

おわり

 WSLをストレスフリーに使う設定のお話でした。

おまけ

XLaunchだけでできる?

 ところで、XLaunchですが、wscriptが実行できるのと同じように、ここからダイレクトにbash -lc lxpanelとか実行することも可能です。XLaunchが起動したコマンドはただちに非表示にされるので、これでもうまくいきそうな気がします。ところが私の環境では、この状態では起動時によくわからない入力警告メッセージが出て、ログアウト時にもきれいに終了しない状態になってしまいました。原因を追いかけてちゃんと設定するとXLaunchだけでいい感じにできるのかもしれません。

X Windowいらない?

 LXTerminalのようなLinuxコンソール的な端末環境が欲しいだけであれば、Windowsにcmd.exeの代替となる端末環境を入れるのがいいみたいです。

Javaでもスケーリングする(高DPI環境用)

 たぶんWSL環境によらない話だと思いますが、Linux Javaアプリ(IntelliJなどのJetBrainツールやEclipse)をスケーリングするときはさらに大変なようです。PyCharmとEclipseを試しましたが、-dpi設定は考慮してくれませんでした。対応方法をメモとして残しておきます。
 まず実行用のJVMはJava9にしてGTK3で動くようにしてください。(Java9でJEP 283: Enable GTK 3 on Linuxがリリースされたためです)
 GTK3は環境変数でスケーリングできます。(Using GTK+ on the X Window System
 関連する環境変数が二つあり、GDK_SCALEは整数しか指定できませんが全てのオブジェクトを拡大できるもので、GDK_DPI_SCALEは小数点付き数値が指定可能でフォントサイズを拡大(縮小も)できるもののようです。一般的にはGDK_SCALEで大きくしつつ、GDK_DPI_SCALEで文字だけ縮小するような使い方をするようですが、試行錯誤の結果、次のような指定でどうにかなりました。

PyCharmの場合(GDK_DPI_SCALEは無意味?)
# GDK_SCALE=2 ./pycharm.sh
Eclipseの場合(GDK_SCALEは無意味?)
# GDK_DPI_SCALE=2 ./eclipse

 調べた限り、どうしてデフォルトでスケーリングしてくれないのか、どうしてこれである程度うまくいくのかさっぱりわからなかったので、環境依存性がどの程度あるのかもわからないのですが、困ったらとりあえずこの2つの環境変数を調整したらどうにかなるんじゃないでしょうか。たぶん。

openSUSE on WSLでLXDEをセットアップ

だいぶん適当なので、余計なものも入るかもしれませんがご愛嬌で。

> echo export DISPLAY=localhost:0.0 >> ~/.bashrc
> echo export LANG=ja_JP.utf-8 >> ~/.bashrc
> su -
Password:
# zypper install MozillaFirefox lxde-common lxpanel-lang lxterminal-lang lxcc yast2-x11 yast2-sudyast2-trans-ja gtk2-theme-* gnome-colors-icon-theme pcmanfm pcmanfm-lang yast2-control-center lxappearance-lang lxappearance-obconf-lang gnome-icon-theme-* yast2-metapackage-handler man-pages-ja gsettings-backend-dconf

ネットワークにつながらない?

 私の環境では何かのタイミングで、/etc/resolv.confが謎のバイナリになりネットワークが不通になることがありました。WSL環境のresolv.confはWSL起動時にWSLが生成してくれる、ということになっていますが、何らかの都合で破損したり生成できなくなることがあるようです。このときは、一度resolv.confを削除して、/etc/wsl.confを作ってみたりすると治るかもしれません。