【キーマッピング】Vimmer が Mac から Windows に乗り換えたときにやったこと

もう1年半前の話になるんですが2022年の5月から、C# （.NET Framework を使ったデスクトップアプリケーション）の案件をやるようになりました。

C#の開発をやるにあたり会社の支給マシンが Mac から Windows に変わりました。
Windows で Vim を使うにあたってキーマッピングの設定で色々と調べて設定するのに時間がかかったので改めてまとめておきます。（個人的なメモに近いです。）

## 前提条件

私はUSキーボード配列が好きでUSキーボード配列の [HHKB](https://happyhackingkb.com/jp/news/promo/01/?utm_source=pmax&utm_medium=cpc&utm_campaign=p-max_v2&gad_source=1&gclid=CjwKCAjwte-vBhBFEiwAQSv_xbGR1tbvFS9wCBsPwqzhjNNqx4cpTKoS87y4tz2Gdnmf-HyNwAzZoxoC8aUQAvD_BwE) を使っています。この後の記事の内容はUSキーボード配列の HHKB を使った場合の話になります。ご了承下さい。

## Mac のときのキーマッピングと実現方法

私は以下のキーマッピングの設定を Mac で行っていました。

1. `command + space` で IME の切り替え（半角英数字/日本語切り替え）を行う
1. コロン `:` とセミコロン `;` を入れ替え
1. `Esc` でIME を OFF （半角英数字入力）にする
1. `control + [` でIME を Off にする


1は、昔の Mac の USキーボードの日本語入力と英字入力の切り替えは「command + space」だった名残で、今もこの癖が抜けないので昔のコマンドを受け継いでいます。

2は Vim 都合です。
Vim だとコロンを押す機会がとても沢山あるので shift キーを押さずにコロンを入力できるように、コロンとセミコロンを入れ替えています。ちなみに C# だと行末にセミコロンが必要ですが、そこは今時だと GitHub Copilot が推測して入れてくれるので、あまり困ることはないです。

3と4 も Vim 都合です。
Vim はノーマルモードと入力モードがあります。入力モードで、IME を ON にして日本語入力した後に  `Esc` キーや `control + [` でノーマルモードに戻ると IME は ON のままです。
このときに、もう一度入力モードに入ろうと `i` を押下すると、IMEが ON なので 「い」と認識されて入力モードに入れません。
なので、`Esc` キーや `control + [` を押下した場合は IME が OFF となるように設定していました。

### 実現方法
上記の設定ですが1の`command + space` で IME の切り替えについては Mac のショートカットキーの割り当てを修正して対応していました。
2~4 については、[Karabiner-Elements](https://karabiner-elements.pqrs.org/) を使って設定していました。 Karabiner-Elements だと上記のような vimmer 向けの設定が既にルールとして公開されているので、それをお借りすれば良いだけです。
この手の記事は沢山あるので、ここでの説明は省略します。

## Windows のときのキーマッピングと実現方法

さて、本題の Windows です。Windows の場合は `command` キーがないので代わりに `control` で代用します。
その他は Mac と同じ設定にします。

1. `control + space` で IME の切り替え（半角英数字/日本語切り替え）を行う
1. コロン `:` とセミコロン `;` を入れ替え
1. `Esc` でIME を OFF （半角英数字入力）にする
1. `control + [` でIME を Off にする

### 実現方法

残念なことに Karabiner-Elements の Windows 版がありませんでした。なので Windows では割とメジャー？な [AutoHotkey](https://www.autohotkey.com/) を使います。

最初にまとめを載せておきます。なお、AutoHotkey の書き方の説明は本記事では省略しています。

```
; Control + space で英語/日本語切り替え
^Space::Send, {vkF3sc029}

; コロンとセミコロンを入れ替え
*vkBA::
    If (GetKeyState("Shift")){
        Send `;
    }
    else {
        Send :
    }
    return

; 以下サイトから拝借
; http://www6.atwiki.jp/eamat/pages/17.html
IME_SET(SetSts, WinTitle="A")    {
    ControlGet,hwnd,HWND,,,%WinTitle%
    if  (WinActive(WinTitle))   {
        ptrSize := !A_PtrSize ? 4 : A_PtrSize
        VarSetCapacity(stGTI, cbSize:=4+4+(PtrSize*6)+16, 0)
        NumPut(cbSize, stGTI,  0, "UInt")  ;   DWORD   cbSize;
        hwnd := DllCall("GetGUIThreadInfo", Uint,0, Uint,&stGTI)
                 ? NumGet(stGTI,8+PtrSize,"UInt") : hwnd
    }
    return DllCall("SendMessage"
          , UInt, DllCall("imm32\ImmGetDefaultIMEWnd", Uint,hwnd)
          , UInt, 0x0283 ;Message : WM_IME_CONTROL
          ,  Int, 0x006  ;wParam  : IMC_SETOPENSTATUS
          ,  Int, SetSts) ;lParam  : 0 or 1
}

~Esc::IME_SET(0)
~^[::IME_SET(0)
```

#### `control + space` で IME の切り替え（半角英数字/日本語切り替え）を行う

https://ahkwiki.net/KeyList を見ると、`vkF3sc029` が全角/半角キーに相当することが分かります。

```
; Control + space で英語/日本語切り替え
^Space::Send, {vkF3sc029}
```

ちなみに AutoHotKey で対応しない手もあるようです。
https://engrholiday.jp/win/msime-ctrl-space/

他にも AutoHotKey で何か設定するなら、AutoHotKey で一元管理しておくと後々困らなくて済みそうです。（実際、自分のHHKBの右windowsキーと右altキーの位置が入れ替わっているんですが何で設定したのか忘れて迷子状態です。HHKBのキーマッピングツールかなぁ？）AutoHotKey のスクリプトファイルがあれば他のPCにも設定できる利点もありそうです。 

#### コロン `:` とセミコロン `;` を入れ替え

こちらは既にまとめて下さっている記事がありましたので、そのまま使わせてもらいました。

https://qiita.com/tomoyuki_golang/items/1a2bf5f537174d745aed


#### `Esc` でIME を OFF （半角英数字入力）にする, `control + [` でIME を Off にする

こちらも既にまとめて下さっている記事がありましたので、そのまま使わせてもらいました。

https://koirand.github.io/blog/2020/vimmer-ahk/

http://www6.atwiki.jp/eamat/pages/17.html に、日本語入力の制御を可能にするための関数群が公開されていましたので `IME_SET` という関数をコピーしています。そして、 `Esc` と `control + [` 押下時に `IME_SET(0)` を実行して IME を Off にしています。

```
; 以下サイトから拝借
; http://www6.atwiki.jp/eamat/pages/17.html
IME_SET(SetSts, WinTitle="A")    {
    ControlGet,hwnd,HWND,,,%WinTitle%
    if  (WinActive(WinTitle))   {
        ptrSize := !A_PtrSize ? 4 : A_PtrSize
        VarSetCapacity(stGTI, cbSize:=4+4+(PtrSize*6)+16, 0)
        NumPut(cbSize, stGTI,  0, "UInt")  ;   DWORD   cbSize;
        hwnd := DllCall("GetGUIThreadInfo", Uint,0, Uint,&stGTI)
                 ? NumGet(stGTI,8+PtrSize,"UInt") : hwnd
    }
    return DllCall("SendMessage"
          , UInt, DllCall("imm32\ImmGetDefaultIMEWnd", Uint,hwnd)
          , UInt, 0x0283 ;Message : WM_IME_CONTROL
          ,  Int, 0x006  ;wParam  : IMC_SETOPENSTATUS
          ,  Int, SetSts) ;lParam  : 0 or 1
}

~Esc::IME_SET(0)
~^[::IME_SET(0)
```

## PC 起動時に AutoHotkey のスクリプトを実行する

AutoHotkey のスクリプトを書いたら実行する必要があります。毎回、手動で実行するのは大変なのでPC起動時に勝手に実行するよう設定します。
設定方法は下記の記事を参考に設定しました。

https://kiryusblog.com/autohotkey-autorun/

## 感想

こういう記事は設定した直後に書かないと辛いなと思いました。特に参考にした記事をメモってなかった場合に辛いですし、なぜその設定になっているのか背景を思い出せない点が辛いです…

いつものことながら先人達の貴重な記事に本当に感謝です。