HHKB 英語配列を自分的 IME に最適化する (外部ソフトウェア不使用)

もう長いこと US キーボードでコードを書いて食べてきたので、体がキー配列を覚えています。愛用する HHKB は当然無刻印です。

困るのはたまに書く日本語で、IME の ON/OFF は、昔は Windows デフォルトの Alt+` でトグルさせていたのですが、ここ数年は [AutoHotKey](https://www.autohotkey.com/) を経由して [alt-ime-ahk](https://github.com/karakaram/alt-ime-ahk) に落ち着いていました。

しかし、職場環境が変わり、セキュリティリスク低減のため、OSS 含むフリーソフトの社用 PC への導入が厳しくなり、[AutoHotKey](https://www.autohotkey.com/) および [alt-ime-ahk](https://github.com/karakaram/alt-ime-ahk)、[PowerToys](https://learn.microsoft.com/ja-jp/windows/powertoys/) の使用を断念せざるを得なくなり ── 実際には申請したら通るんでしょうが、なんか……つまんなくてイヤ 笑 ──、Alt+` や、最近設定できるようになった Ctrl＋Space といったトグル方式に戻ろうとしたものの、明確に ON/OFF を切り替えられないのは著しく非効率で、もはや戻ることができないことを再認識し、外部ソフトに頼らず、Windows が持つ機能だけで ON と OFF の切り替えを模索した結果のメモです。

同様の情報はあちこちで入手できますが、私の理解をまとめるために記事にしました。

~~結論から言うと、次のようにすることで落ち着きました。~~
**この記事の最下部に最終結論が書いてあります。AX キーボード ドライバへの入れ替えはやめました 笑**

~~**方法**~~
~~* AX キーボードのドライバに切り替え~~
~~* レジストリの書き換え~~

~~**キーアサイン**~~
~~* Alt (Right) → IME ON~~
~~* Alt (Left) → IME OFF~~
~~* Shift (Right) → Alt (Left)~~

[alt-ime-ahk](https://github.com/karakaram/alt-ime-ahk) とほぼ同じですが、Alt とのコンビネーションができなくなるので、ほぼ使用することのない右 Shift を Alt に割り当てています。

使用している OS は Windows 10 と Windows 11 で、日本語キーボードのラップトップ環境を含む複数の環境で同一の状況を作ることができています。
すべて HHKB 英語配列キーボードを接続しています。

# 1. AX キーボードドライバへの差し替え

最終的にレジストリをいじるのに、なぜ AX キーボードドライバに差し替える必要があるのか？と思ってしまうところですが、AX キーボードでないと IME の Convert (0x5B) と Non-Convert (0x5A) の Scancode (後述) を送信しない仕様のため、このドライバに差し替えてやることにより、Windows が「今使っているのは AX キーボードなので、Convert と Non-Convert をサポートする」と認識させる必要があるようです。

つまり、AX キーボードドライバに差し替えず、レジストリだけで Convert (0x5B) や Non-Convert (0x5A) を偽装しても、適切に動作しません。

なお、[AutoHotKey](https://www.autohotkey.com/)/[alt-ime-ahk](https://github.com/karakaram/alt-ime-ahk) や [PowerToys](https://learn.microsoft.com/ja-jp/windows/powertoys/) の Keyboard Manager のコードをちょっと覗いてみたところでは、それらは [SetWindowsHookEx](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-setwindowshookexa) + WH_KEYBOARD_LL を使用しており、仮想キーの VK_CONVERT や VK_NONCONVERT を出してくれるので、キーボードに依存しない、ということのようですが、その一方で、ログインしてこれらアプリを起動しておかないとキーアサインが変わらないということになります。

AX キーボードドライバへの差し替えは、以下に説明する手順で行いました。


## 1.1. AX キーボードドライバの存在確認

AX キーボードドライバは、少なくとも Windows 10、Windows 11 にあることは既知でしたが、
念のため、`C:\Windows\System32\kbdax2.dll` が実際に存在することを確認しておくのが良いと思います。
これが差し替えるドライバ本体です。

## 1.2. 差し替え作業

レジストリエディタを用いて、下記レジストリを操作することにより、差し替えを行うことができます。
`HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\i8042prt\Parameters`

| 名前                        | 初期値                                   | 変更後の値   |
|-----------------------------|-----------------------------------------|-------------|
| LayerDriver JPN             | kbd106.dll, kbd101a.dll など **(注1)**  | kbdax2.dll   |
| OverrideKeyboardIdentifier  | PCAT_101KEY, PCAT_106KEY など **(注2)** | AX_105KEY    |
| OverrideKeyboardSubtype     | 0                                       | 1           |
| OverrideKeyboardType        | 7                                       | 7 (変更不要) |

<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1859956/c7580053-743a-2be1-7f7d-24fb0426cf9f.png">

編集を終えて再起動すると (再起動せずにこのままキーマップの変更に進んでも構いません)、右 Alt が右 Alt として機能しなくなります。

**(注1, 注2)**
その時の Windows のキーボードの設定が、日本語キーボードか、US キーボードかによります。
これは、Windows の下記で確認できます。
[時刻と言語] → [言語と地域] → [日本語] → [言語のオプション] → [キーボード] → [キーボードレイアウト] → [レイアウトを変更する]
なお、上記編集後に再起動すると、該当箇所の表示は次のようになり、`接続済みキーボードレイアウトを使用する` という項目が追加され、これが選択されています。

<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1859956/512734aa-1e97-3cde-09ee-30a10014b72a.png">

以上の作業で、Convert (0x5B) や Non-Convert (0x5A) をキーマップで使えるようになりました。

私の場合、どの PC でも以上の作業で差し替えが終わるのですが、外付けキーボードを使用せず、ラップトップのキーボードをそのまま使用する場合には、さらに使用しているキーボードのレジストリに、下記の項目を追加しないと動作しないケースがあるようです。

`HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\HID\{キーボードのハードウェア ID}\{ID}\Device Parameters`

| 名前                     | データ型  | 値 |
|-------------------------|-----------|----|
| OverrideKeyboardSubtype | REG_DWORD | 1  |
| OverrideKeyboardType    | REG_DWORD | 7  |



# 2. キーマップの変更

キーマップの変更はレジストリに値を書き込むことで行うことができます。
レジストリに書き込む変更前、変更後のキーは、各物理キーに割り当てられている Scancode というものを使います。
すでに触れた、Convert (0x5B) や Non-Convert (0x5A) といった 0x5A, 0x5B が、AX キーボードがサポートする Scancode となります。

各キーの Scancode の確認と、レジストリによる割り当ては、以下の手順に従います。

## 2.1. Scancode

キーマップの変更は Scancode をレジストリに書き込むことにより行います。
Scancode についての仕様書は下記より入手できます。
https://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc

この資料から、例えば次のキーの Scancode が分かります。

| キー | Scancode |
|---|---|
| 右 Shift (R SHIFT) | 36 |
| 左 Alt (L ALT) | 38 |
| 右 Alt (R ALT) | E0 38 |

## 2.2. キーマップの変更作業

Scancode が分かったら、それを使ってキーのアサインを変更します。
変更は、`HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout` 以下に `Scancode Map` というバイナリ値を追加することで行うことができます。
このキーと並んで `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts` というキーもありますが、こちらには触れません。

## 2.3. バイナリ値の作り方

[ここに Microsoft が提供する仕様が書かれています。](https://learn.microsoft.com/ja-jp/windows-hardware/drivers/hid/keyboard-and-mouse-class-drivers#scan-code-mapper-for-keyboards)
記載の通り、ここは 4 バイトずつ値を入力する必要があり、各々の 4 バイトはリトルエンディアンを意識して書く必要があります。つまり、レジストリでは 4 バイトを逆にして、下位バイトから記述します。また、最後に 0x00000000 (Null値) を加えて終端とします。

具体的な記述方法は以下のようになります。

* 最初に 00 を 8 つ加えます。これはヘッダーで、固定値となります。
* 次に、これ以降に記述するマッピングを変更するキーの個数に加えて、終端の Null 値を加えた数を書きます。3 個のキーの変更を書くなら、最後の Null 値 (0x00000000) を加えて、0x00000004 とする必要があります。レジストリへの記載は、全バイトをひっくり返して、04, 00, 00, 00 とします。
* 変更するキーの Scancode と変更後のキーの Scancode を 2 バイトずつ並べます。レジストリへの記入の際には、全バイトをひっくり返すのは同じです。
* 最後に Null 値 (0x00000000) を加えます。

例)

| 値 | 意味 | レジストリに書く時の値 |
|----|------|----------------------|
| 0x00000000 | ヘッダー (固定値) | 00, 00, 00, 00 |
| 0x00000000 | ヘッダー (固定値) | 00, 00, 00, 00 |
| 0x00000004 | 変更するキーの個数 + 1 = 3 + 1 = 4| 04, 00, 00, 00 |
| 0x00360038 | 右 Shift (0x0036) を 左 Alt (0x0038) へ | 38, 00, 36, 00 |
| 0xe038005b | 右 Alt (0xE038) を Convert (0x005B) へ | 5b, 00, 38, e0 |
| 0x0038005a | 左 Alt (0x0038) を Non-Convert (0x005A) へ | 5a, 00, 38, 00 |
| 0x00000000 | 終端の Null 値 | 00, 00, 00, 00 |

最終的にレジストリに書き込む値:
(上記の値を連ねて)
```
00, 00, 00, 00, 00, 00, 00, 00, 04, 00, 00, 00, 38, 00, 36, 00, 5b, 00, 38, e0, 5a, 00, 38, 00, 00, 00, 00, 00
```

なお、値は 16 進数で記述する必要がありますが、アルファベットの大文字小文字の別はありません。


私は、上記「例」に加えて、Capslock (Scancode=0x003A) を左 Ctrl (Scancode=0x001D) に置き換え、次のようにしています。

| 値 | 意味 | レジストリに書く時の値 |
|----|------|----------------------|
| 0x00000000 | ヘッダー (固定値) | 00, 00, 00, 00 |
| 0x00000000 | ヘッダー (固定値) | 00, 00, 00, 00 |
| 0x00000005 | 変更するキーの個数 + 1 = 4 + 1 = 5 | 05, 00, 00, 00 |
| 0x003A001D | Capslock (0x003A) を 左 Ctrl (0x001D) へ | 1d, 00, 3a, 00 |
| 0x00360038 | 右 Shift (0x0036) を 左 Alt (0x0038) へ | 38, 00, 36, 00 |
| 0xE038005B | 右 Alt (0xE038) を Convert (0x005B) へ | 5b, 00, 38, e0 |
| 0x0038005A | 左 Alt (0x0038) を Non-Convert (0x005A) へ | 5a, 00, 38, 00 |
| 0x00000000 | 終端の Null 値 | 00, 00, 00, 00 |

```
00, 00, 00, 00, 00, 00, 00, 00, 05, 00, 00, 00, 1d, 00, 3a, 00, 38, 00, 36, 00, 5b, 00, 38, e0, 5a, 00, 38, 00, 00, 00, 00, 00
```

<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1859956/26ce52d3-8a08-7419-d576-926e9a529e7a.png">

再起動すれば設定したキーアサインになっているはずです。


# 3. もっと簡単に

以上の作業を 1 台の PC に対して行う分には問題ないでしょうが、2 台、3 台と行うのはげんなりします。
この場合、下記のような .reg ファイルを用意して実行してやればラクになります。

```
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\i8042prt\Parameters]
"LayerDriver JPN"="kbdax2.dll"
"LayerDriver KOR"="kbd101a.dll"
"OverrideKeyboardIdentifier"="AX_105KEY"
"OverrideKeyboardSubtype"=dword:00000001
"OverrideKeyboardType"=dword:00000007

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout]
"Scancode Map"=hex: 00,00,00,00,00,00,00,00, 05,00,00,00, 1D,00,3A,00, 38,00,36,00, 5b,00,38,e0, 5a,00,38,00, 00,00,00,00
```

Windows 12 以降のことも考えて、`kbdax2.dll` の存在チェックを含めてスクリプト化しておくと良いかもしれません。
でもその時にはもっとスマートな方法が Windows に実装され、この記事が全く役に立たなくなることを願います。


# 追記

左 Alt はタスク切り替え時に無意識に多用していたことに気づいたので、最終的 (だと良いんですけど) に、右 Shift を Non-Convert にしました。

# 最終結論

私は vim を使うのですが、その場合、Esc または Ctrl+[ でノーマルモードへ抜ける必要があります。
ノーマルモードでは IME が OFF になるのが期待値です。
……といったことも加味すると、外部ソフトウェアに依存せず、IME もストレスなく切り替えられるのは以下のような構成となりました。

* AX キーボード ドライバへの差し替えをやめて、単純に 101 キーボードにする
* (Windows の設定画面から) 「前のバージョンの IME」を使用するようにする
* レジストリで、右 Shift の Scancode を F13 (0x64) にアサイン
* IME の「詳細設定」で F13 に IME ON をアサインする
* IME の「詳細設定」で Esc と Ctrl+[ に IME OFF をアサインする

右 Shift の Scancode を F13 (0x64) にアサインする .reg ファイルは以下の通り。(CapsLock を左 Ctrl にするのも含んでいます)

```
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout]
"Scancode Map"=hex: 00,00,00,00, 00,00,00,00, 03,00,00,00, 1d,00,3a,00, 64,00,36,00, 00,00,00,00
```

これで、右 Shift で IME ON、Esc または Ctrl+[ で IME OFF になります。しばらくこれでやってみよう。