X Server (X410)を導入して、いい感じのターミナルでWSL2を使おう

WSL2いいですよね。便利ですよね。
ですが、WSLのデフォルトのコンソールは
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271892/5ac8d907-a01f-658b-d8a7-9303b3a9ca61.png" alt="デフォルトのターミナル" width=50%>
こんな感じなので、普段使いのLinuxデスクトップ環境とは操作感が違っていて戸惑うこともあるのではないでしょうか。

特に、

* コマンドを補完しようとするとピコピコ音が鳴るのが気になる
* クリップボードからの貼り付けがホイールクリックではなく右クリックなので違和感がある
* 日本語の表示がずれている気がする
* タブ機能がない

といった点から、使い慣れた端末エミュレータを使いたいと思うことも多いと思います。
そこで、X Serverを使ってLinuxのGUIアプリケーションを動かせるようにして、Linux向けのターミナルをWindows上で利用することでより快適にWSL2を使えるようにしましょう。

この記事では、例としてWindows10にWSL2(Debian)がインストールされている状態から、X410を導入してxfce4-terminalを起動するまでを紹介していきます。

### WSL2の導入確認
この記事の本題ではないので、WSL2自体の導入については他の記事に譲ります。

PowerShellコンソールで、

```powershell
wsl
```

と入力するとbashが立ち上がることと、同じくPowerShellコンソールで

```powershell
wsl --list --verbose
```

と入力するとインストールされているディストリビューションが表示され、 `VERSION` 欄の値が `2` になっていることを確認してください。

WSLがまだ有効化できていない場合や、 `VERSION` が `1` になっている(WSL1のままになっている)場合は、「WSL2 インストール」などで検索してWSL2の導入を行いましょう。

記事中ではディストリビューションとしてDebianを使用していますが、インストールするディストリビューションはお好みで選択してください。 [^1]
[^1]: この記事では`bash`と`apt`パッケージマネージャを前提として話を進めるため、UbuntuまたはDebianであればスムーズに進めていけるはずです。それ以外のディストリビューションを使用している場合は適宜読み替えてください。

### X Server (X410) のインストール
X Serverは [X Window System (X11)](http://ss.sguc.ac.jp/~rider/unix/X.html) のサーバー側で、入力の受け付けと画面表示を担当する部分です。
普段Linux上でデスクトップ環境を使用する場合でも、内部的にはX11のサーバーとクライアントが通信を行うことによって画面表示を行っています。
Windows上でX Serverを動かしてWSL2側のX Clientと通信することで、WSL2で動作しているGUIアプリケーションをWindows側で表示することができるようになります。

ここでは、[X410](https://x410.dev/)というソフトをインストールしてみます。
X410は[Microsoftストア上で販売](https://www.microsoft.com/ja-jp/p/x410/9nlp712zmn9q)されており、定価は6000円ほどですが恒常的に80%~90%程度の割引がなされており、実質的に1000円前後で購入できる有料ソフトです。
うまく動かせるかわからないうちからお金を払いたくない場合や、たまたま定価で売られている期間だった場合には、無料試用版をインストールするか、[VcXsrv](https://sourceforge.net/projects/vcxsrv/)などの無料で利用できるXサーバーを代わりにインストールしてください。

MicrosoftストアからX410をインストールできたら、ストア内のライブラリかスタートメニューからX410を起動しましょう。
何もウィンドウが表示されないかもしれませんが、タスクトレイに"X"と書かれたアイコンが増えていれば起動できています。

WSL2はWSL1とは違ってlocalhostではなく別のIPで動作している扱いになるので、X410の設定を変更し、外部との通信を有効にする必要があります。
X410のアイコンを右クリックし、表示されるメニューから"Allow Public Access"の項目を選択してください。
確認のポップアップでOKを押すと、Windowsファイアウォールの確認画面が表示されるので、「パブリック ネットワーク」の欄にチェックが入っている状態でアクセス許可を選択してください。
再度X410のアイコンを右クリックして、"Allow Public Access"の項目にチェックが入っていることを確認しましょう。

また、X410のアイコンにマウスを乗せると、`X410 @ DISPLAY=127.0.0.1:0.0` のように表示されると思います。
この末尾の`:`以降の数値は、ディスプレイIDとスクリーンIDを表しています。
特に手を加えていなければ `0.0` になっているはずですが、この部分に表示されている値を確認しておきましょう。

X Serverが起動していない状態ではGUIアプリケーションを表示できないので、X410はWindowsのスタートアップに登録しておくことをおすすめします。
エクスプローラーに`%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup`と入力して移動し、表示された場所にプログラムメニューのX410のアイコンをドラッグ&ドロップすることで、Windowsの起動時にX410が立ち上がるようになります。

### GUIアプリケーションの表示

X Serverが起動できたので、実際にGUIアプリケーションをWindows上に表示してみましょう。
今回はxfce4-terminalを導入したいので、WSL2内にインストールしておきます。

```bash
$ sudo apt update
$ sudo apt install xfce4-terinal
```

gnome-terminalやterminatorなど、お好みのターミナルを使用したい場合はそちらをインストールしておきましょう。
この章ではGUIが表示できることを確認するだけなので、かわりにxeyesとかでも良いです。

インストールが完了したら動作確認を行います。
Windows側で起動しているX Serverに接続する必要があるのですが、WSL2ではLinux側からもlocalhostでWindowsにアクセスすることができないので、まずはホストとして割り当てられたIPアドレスを確認します。

```bash
$ cat /etc/resolv.conf
```

で `/etc/resolv.conf` の内容を表示すると、

```:/etc/resolv.conf
# This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry to /etc/wsl.conf:
# [network]
# generateResolvConf = false
nameserver xxx.xxx.xxx.xxx
```

のように表示されると思います。
この`xxx.xxx.xxx.xxx`の部分に表示されているIPアドレスを用いることでWindows側に通信することができます。

https://github.com/microsoft/WSL/issues/4106#issuecomment-501885675 を参考にすれば `/etc/resolv.conf` の内容を取得できるので、

```bash
$ export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0.0
$ xfce4-terminal
```
と入力しましょう。
(上で確認したX410のIDが`0.0`ではなかった場合、末尾の`0.0`の部分を以後適宜読み替えてください)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271892/be4239b6-3f8c-ac93-24e9-1de33912fac7.png)

Windowsのデスクトップ上にLinuxのGUIアプリケーションが表示されていて非常にいい感じですね。
これでLinuxのターミナルがWindowsの上で表示できるようになったので、次はターミナルが使いやすくなるように整えていきます。

### ターミナルを簡単に起動できるようにする [^2]
[^2]: または: Windowsの深淵を覗く

ターミナルの表示自体はできましたが、WSL2のコンソールを一度立ち上げてからコマンド入力で新しくターミナルを起動するのは本末転倒感があります。

![terminal shortcut in start menu](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271892/d82b9e6e-d71e-7440-4eb1-1555b7cd5de9.png)

このように、スタートメニューにターミナルを登録しておき、ここから簡単に呼び出せるようにしていきましょう。

#### バッチファイルの作成
(※実際に作業する際、この節の内容は読み飛ばして構いません)

まずはWSL2に入ってからターミナルを起動するのではなく、Windows側のコマンドラインから直接ターミナルを開けるようにしたいところです。

たとえば、`/etc/resolv.conf`に記載されているIPアドレスが`xxx.xxx.xxx.xxx`だったとして、

```winbatch
wsl bash -lc "export DISPLAY=xxx.xxx.xxx.xxx:0.0 && xfce4-terminal"
```

という内容で.batファイルを作成して実行すればxfce4-terminalが起動することが確認できると思います。

ちなみに、このコマンドはなぜかPowerShellのコマンドライン上ではエラーになり、

```winbatch
wsl bash -lc 'export DISPLAY=xxx.xxx.xxx.xxx:0.0 && xfce4-terminal'
```

と書かなければ動かないようです。
この場合、.batファイルとして実行するとエラーになります。

つまり、**シングルクォートで囲うとPowerShell上では実行できるが.batファイルにするとエラーになり、ダブルクォートで囲うと.batファイルとしては実行できるがPowerShell上ではエラーになる**ようです。コマンドプロンプト/PowerShellなんもわからん…

ひとまずダブルクォートで囲っておけば.batファイルとしては実行できるようなので先に進みましょう。
IPアドレスを直書きしていた部分を元に戻して、

```winbatch
wsl bash -lc "export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0.0 && xfce4-terminal --default-working-directory=$HOME"
```

という内容で適当な名前の.batファイルに保存し、ダブルクリックで実行することでターミナルが起動できるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271892/da76289a-ca34-14cf-998f-873089aa9758.png)
ですが、このようにターミナルの裏にcmd.exeの黒い画面が開いたままになっています。
邪魔だからと閉じようとするとxfce4-terminalごと終了してしまうため、これをどうにかしたいところです。

そこで、.batファイルのかわりに.jsファイルを用意します。
**JavaScriptファイルではありません。JScriptファイルです。**

#### JScriptファイルの作成

JScriptというのは、大昔のInternet Explorerに搭載されていたJavaScriptによく似た何かのことです。
詳しいことは聞かないでください。
Windows上で.jsファイルを実行すると、WSH (Windows Script Host)と呼ばれる実行環境の上でJScriptを動かすことができます。

このJavaScriptによく似た過去の遺物はWindowsに対する強力な操作が可能で、ウィンドウを非表示にする機能も含んでいるので、これを利用してcmd.exeのウィンドウが表示されないようにします。

`xfce4-terminal.js`という名前のファイルを以下の内容で作成します。[^3]

```javascript:xfce4-terminal.js
new ActiveXObject("WScript.Shell").Run("wsl bash -lc \"export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0.0 && xfce4-terminal\"", 0);
```

[^3]: 当然ですが、わざわざbashにコマンドとして`export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}')` の行を与えなくても、`~/.bashrc`や`~/.profile`にexportコマンドを書いておくことでもX Serverに接続できます。むしろそちらの方が楽かもしれません。

`new ActiveXObject("WScript.Shell").Run()`の第2引数に`0`を指定することで、ウィンドウを非表示にした状態でコマンドを実行できます。
JScript中に文字列でコマンドプロンプトの実行コマンドを書き、その中に文字列でbashコマンドを書いたうえでコマンド展開をして、しかもそこにawkのコマンドが文字列で書かれているという恐ろしい入れ子構造になっていますが、奇跡的にエスケープがうまくいって期待通りに動作してくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271892/eb84d62f-e269-22cf-6a7d-8f4912c9a678.png)

これでxfce4-terminalのウィンドウだけを表示することができるようになりました。
このようにしてxfce4-terminalを実行すると、xfce4-terminal.jsファイルが置かれている場所をカレントディレクトリとして開くようです。
最初に開くディレクトリを変更したい場合には、`xfce4-terminal` に `--default-working-directory`オプションを指定することで設定が可能です。

たとえば、

```javascript:xfce4-terminal.js
new ActiveXObject("WScript.Shell").Run("bash -lc \"export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0.0 && xfce4-terminal --default-working-directory=$HOME\"", 0);
```

とすることでWSL2側のホームディレクトリを開くことができます。

#### JScriptファイルの関連付けの設定
Windows10のデフォルトの状態では、.jsファイルをダブルクリックするとWSHが動くようになっています。
ですが、JavaScriptでの開発のためにNode.jsで実行されるように関連付けを変更していたり、VSCodeなどのエディタが開くようにしている場合があるかもしれません。

その場合には、ダブルクリックでJScriptが実行されるように適当な拡張子をでっちあげるのが良いでしょう。
たとえば、`xfce4-terminal.jscript` というファイル名でJScriptファイルを作ります。

コマンドプロンプトを管理者として実行し、以下のコマンドを入力してください。

```winbatch
assoc .jscript=JSFile
```

これで.jscriptという拡張子がWSHに関連付けられます。
この作業を行った場合、以後のxfce4-terminal.jsというファイル名を適宜読み替えてください。

#### スタートメニューへの登録
`xfce4-terminal.js`ファイルが作成できたら、今度はこれをスタートメニューから起動できるようにします。

`xfce4-terminal.js`を右クリックして、「ショートカットの作成」でショートカットを作ります。
このショートカットファイルの名前を`xfce4-terminal.js - ショートカット` から `xfce4-terminal`に変更したり、適当にアイコンを変えたりして見た目を整えたら、エクスプローラーに`%APPDATA%\Microsoft\Windows\Start Menu\Programs`と入力して移動し、そこにショートカットファイルを移動させてください。
これでWindowsメニュー内にショートカットの名前でファイルが登録されるので、右クリックで「スタートにピン留めする」を選ぶことができます。

もともとの`xfce4-terminal.js`は、うっかり削除や移動をしてしまわないような場所に置いておきましょう。

これで快適にターミナルが呼び出せるようになりました。

### 日本語の表示・入力に関する設定
ターミナル自体はほぼ完全に使えるようになりましたが、このままの状態では日本語の表示・入力ができないため、最低限の日本語環境を整えていきます。

#### 日本語の表示
日本語用のフォントがインストールされていない場合、ターミナルで日本語を表示することができません。
その場合はお好みのフォントをインストールすることで、日本語が表示されるようになります。

たとえば、

```bash
$ sudo apt install fonts-noto
```

でNotoフォントをインストールすることができます。


#### setxkbmapを使用したキーボードレイアウトの変更

デフォルトの状態ではキーボードのレイアウトがUSキーボードのものとなっています。
そのため、日本語配列のキーボードを使用している場合は設定を変更する必要があります。

X410の公式ドキュメント https://x410.dev/cookbook/keyboard-layout/ を参考にして、

```bash
$ sudo apt install x11-xkb-utils xkb-data
$ setxkbmap jp -model pc106
```

とすることで、日本語キーボード(106キー)のキー配列を使用できるようになります。

この設定はシャットダウン後に元に戻ってしまうので、

```bash
setxkbmap jp -model pc106
```

の行を `~/.bashrc`などに追記するか、`xfce4-terminal.js`を編集して

```javascript:xfce4-terminal.js
new ActiveXObject("WScript.Shell").Run("bash -lc \"export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0.0 && setxkbmap jp -model pc106 && xfce4-terminal\"", 0);
```

のようにします。

#### 日本語の入力

日本語の配列にキーボードレイアウトを変更したら、次は日本語を入力するための設定を行います。
Windows側のIMEから日本語を直接入力することはできないので、Linux側にIMEを導入していきます。

```
$ sudo apt install fcitx-mozc dbus-x11
```

以下のコマンドを実行すると、fcitxの設定用ウィンドウが表示されます。[^4]

[^4]: https://qiita.com/dozo/items/97ac6c80f4cd13b84558 https://kazblog.hateblo.jp/entry/2018/05/28/221242

```bash
$ export GTK_IM_MODULE=fcitx
$ export QT_IM_MODULE=fcitx
$ export XMODIFIERS=@im=fcitx
$ fcitx
$ fcitx-configtool
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271892/2b9cf196-c3e4-3ef1-d970-45b97b95f7bb.png)

左下の"-"ボタンを押し、最初に設定されている`Keyboard - Japanese`の項目を削除して、"+"ボタンを押して一覧の中から`Mozc`を探して追加します。
一覧から見つけられない場合は、"Only Show Current Language" のチェックを外すと表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271892/e3b1db8d-1f89-c929-01b0-c1f1a448a6d0.png)

設定ウィンドウを閉じると、半角/全角キーで日本語入力が可能になっていると思います。

Mozcの入力設定を行いたい場合は、

```bash
$ /usr/lib/mozc/mozc_tool --mode=config_dialog
```
で設定ダイアログを開くことができます。

あとは、ターミナルの起動時に自動的にIMEが使用できる状態になるように、`xfce4-terminal.js`を以下のように編集します。

```javascript:xfce4-terminal.js
new ActiveXObject("WScript.Shell").Run("bash -lc \"export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0.0 && setxkbmap jp -model pc106 && export GTK_IM_MODULE=fcitx && export QT_IM_MODULE=fcitx && export XMODIFIERS=@im=fcitx && fcitx && xfce4-terminal\"", 0);
```

以上で日本語入力の設定は完了です。

### おわりに

この記事では、Linux上で動作するターミナルをWSL2環境でも利用できるようにする方法を紹介しました。
Windowsの開発環境を普段使いのLinuxデスクトップ環境に近づけていくことで、ぜひ快適にWSL2を利用できるようにしてみてください。


なお、この記事は [DeNA 21 新卒 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/dena-21-shinsotsu) の2日の記事になっています。
なので、記事の末尾に宣伝が表示される仕様となっています。よろしくお願いします。

宣伝:

> [DeNA 公式 Twitter アカウント @DeNAxTech](https://twitter.com/DeNAxTech) では、 Blog記事だけでなく色々な勉強会での登壇資料も発信してます。ぜひフォローして下さい！
> <a href="https://twitter.com/intent/follow?screen_name=DeNAxTech">Follow @DeNAxTech</a>