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などディストリビューションを選ぶこともできるようになり、たいへん導入しやすくなりました。

* [Windows版Linuxの導入でつまずかない方法](http://itpro.nikkeibp.co.jp/atcl/column/17/100300411/101000003/)

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

<img width="960" alt="desktop.png" src="https://qiita-image-store.s3.amazonaws.com/0/25493/a4cd6a12-43ac-8d8a-9eaa-997430e1befc.png">
どれがLinuxアプリでどれがWindowsアプリかわからない…といいんですが。

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

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

* [俺の Bash on Windows10 環境](https://qiita.com/shima_529/items/9e818af727fb936771b6)
* [Bash on Ubuntu on Windows + VcXsrv + RubyMine でWindowsでも快適なRails開発環境を作ろう](https://qiita.com/fukuramikake/items/283b817c16725af79a28)

　なお、X Window Serverは[VcXsrv](https://sourceforge.net/projects/vcxsrv/)、デスクトップ環境はLXDEで、スクリーンショットもそれに準じています。環境に応じて随時読み替えてください。ちなみに、[Xming](http://www.straightrunning.com/XmingNotes/)は最新版（寄付版）を持っていないのであれば、VcXSrvの方がだいたいすべての面でいいです。

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

```shell-session
# sudo ln -s /mnt/c/Windows/Fonts /usr/share/fonts/windows
# sudo fc-cache -fv
```
　これでWindows10フォントがLinuxから選べるようになったと思います。

<img width="326" alt="selectfont.png" src="https://qiita-image-store.s3.amazonaws.com/0/25493/4556a2b2-eb2b-e6b8-7ca3-1b555675ec83.png">

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

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

## VcXsrvのスケーリングを無効にする
　exeファイルのプロパティからスケーリングを無効にします。
<img width="302" alt="vcxsrv.png" src="https://qiita-image-store.s3.amazonaws.com/0/25493/aa03cfa4-a8e6-0d26-2483-c263676fc794.png">

## 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`などと実行しても、こんな画面が残ってしまいます。
<img width="398" alt="bash-window.png" src="https://qiita-image-store.s3.amazonaws.com/0/25493/9fa183f9-1959-5798-047a-49cb988252d8.png">
この画面を消します。

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

```js: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.js`は`C:\\Program Files\VcXsrv`に入れておきます。

<img width="382" alt="startprog.png" src="https://qiita-image-store.s3.amazonaws.com/0/25493/ba59d548-1c8c-62a3-f66e-d334a03c2a24.png">
Start a programを選んで
<img width="382" alt="setprog.png" src="https://qiita-image-store.s3.amazonaws.com/0/25493/be951bd7-04bc-7470-7354-5aa1f07b7caa.png">
ここに入力すればいいのですが、厳しい文字数制限があって、入れることができません。
　なので、出来上がったxlaunchファイルをテキストエディタで次のように編集します。実態はXMLファイルなので、編集は容易です。`LocalProgram`属性がそれです。

```xml: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も落ちます。
<img width="224" alt="lxpanel.png" src="https://qiita-image-store.s3.amazonaws.com/0/25493/021c3974-1aad-6076-ab5a-e55202986c7b.png">
※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の代替となる端末環境を入れるのがいいみたいです。

* [Bash on Ubuntu on Windows の最高の端末環境を教えてやるからちょっと来い。](http://mattn.kaoriya.net/software/bow/20160824234928.htm)

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

```shell-session:PyCharmの場合（GDK_DPI_SCALEは無意味？）
# GDK_SCALE=2 ./pycharm.sh
```
```shell-session:Eclipseの場合（GDK_SCALEは無意味？）
# GDK_DPI_SCALE=2 ./eclipse
```
　調べた限り、どうしてデフォルトでスケーリングしてくれないのか、どうしてこれである程度うまくいくのかさっぱりわからなかったので、環境依存性がどの程度あるのかもわからないのですが、困ったらとりあえずこの2つの環境変数を調整したらどうにかなるんじゃないでしょうか。たぶん。

### openSUSE on WSLでLXDEをセットアップ
だいぶん適当なので、余計なものも入るかもしれませんがご愛嬌で。

```shell-session
> 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```を作ってみたりすると治るかもしれません。

* [RS4のWindows Subsystem for Linuxでのwsl.confによる初期設定](http://ascii.jp/elem/000/001/634/1634120/)
* [Automatically Configuring WSL (Microsoft)](https://blogs.msdn.microsoft.com/commandline/2018/02/07/automatically-configuring-wsl/)