Windows 11でKeyhacとEmacsキーバインド

Windows11上のメモ帳とChromeについて、以下のショートカットキーを使えるようにKeyhacで設定する。

- 左矢印：Ctrl+b
- 右矢印：Ctrl+f
- 上矢印：Ctrl+p
- 下矢印：Ctrl+n
- 行末へジャンプ：Ctrl+e
- 行頭へジャンプ：Ctrl+a
- バックスペース：Ctrl+h
- デリート：Ctrl+d
- カーソルより右を削除：Ctrl+k
 
※CapsLockとCtrlを入れ替える方法については省略

# Keyhacのインストール
以下より最新版のzipファイルをダウンロード、展開し、その中にあるkeyhac.exeを実行するだけ。
https://github.com/crftwr/keyhac-win/releases

https://sites.google.com/site/craftware/keyhac-ja

うまくダウンロードできない場合は、[Microsoftのページ](https://www.microsoft.com/en-us/download/details.aspx?id=48145)より新しめのVisual Studioランタイムパッケージをインストールする。

# Keyhacの設定（config.py）

インストール後展開して、その中にある_config.pyをconfig.pyという名前に変更し以下のように記述する。

```python:config.py
import pyauto
from keyhac import *
    
def configure(keymap):
    keymap.defineModifier(28, "User0")  # 変換キー（コード28）をモディファイアキー0番として定義。（変換キーのキートップをCMDに変更した）
    
    # config.pyをVSCodeで開く
    keymap.editor = "C:\\Users\\YamadaTaro\\AppData\\Local\\Programs\\Microsoft VS Code\\Code.exe"

    keymap_global = keymap.defineWindowKeymap()
    keymap_global["User0-W"] = "Alt-F4"  # 右上のばつ

    # Windowsのシャットダウン
    keymap_shut = keymap.defineWindowKeymap(exe_name="explorer.exe", class_name="ComboBox")
    keymap_shut["LC-P"] = "Up"
    keymap_shut["LC-N"] = "Down"    

    # メモ帳用の設定
    keymap_notepad = keymap.defineWindowKeymap(exe_name="notepad.exe", class_name="RichEditD2DPT")

    keymap_notepad["LC-P"] = "Up"                  # Move cursor up
    keymap_notepad["LC-N"] = "Down"                # Move cursor down
    keymap_notepad["LC-F"] = "Right"               # Move cursor right
    keymap_notepad["LC-B"] = "Left"                # Move cursor left
    keymap_notepad["LC-A"] = "Home"                # Move to beginning of line
    keymap_notepad["LC-E"] = "End"                 # Move to end of line
    keymap_notepad["LC-H"] = "Back"                # Backspace
    keymap_notepad["LC-D"] = "Delete"
    keymap_notepad["User0-A"] = "C-A"
    keymap_notepad["User0-F"] = "C-F"               # search
    keymap_notepad["LC-K"] = "S-End", "Back"        

    keymap_notepad["User0-C"] = "C-C"
    keymap_notepad["User0-X"] = "C-X"
    keymap_notepad["User0-V"] = "C-V"

    # Chrome用の設定
    keymap_chrome = keymap.defineWindowKeymap(exe_name="chrome.exe", class_name="Chrome_WidgetWin_1")
    
    keymap_chrome["LC-P"] = "Up"
    keymap_chrome["LC-N"] = "Down"
    keymap_chrome["LC-F"] = "Right"
    keymap_chrome["LC-B"] = "Left"
    keymap_chrome["LC-A"] = "Home"
    keymap_chrome["LC-E"] = "End"
    keymap_chrome["LC-H"] = "Back"
    keymap_chrome["LC-D"] = "Delete"
    keymap_chrome["User0-A"] = "C-A"            # select all
    keymap_chrome["User0-F"] = "C-F"            # search
    keymap_chrome["LC-K"] = "S-End", "Back"
    keymap_chrome["LC-S-Back"] = "LC-S-Delete" 
    keymap_chrome["User0-Y"] = "LC-H"           # 履歴表示（Ctrl+H）をCMD+Yに変更

    keymap_chrome["User0-T"] = "LC-T"  # 新規タブ作成
    #keymap_chrome["User0-W"] = "LC-W"  # タブを削除

    keymap_chrome["User0-C"] = "C-C"
    keymap_chrome["User0-X"] = "C-X"
    keymap_chrome["User0-V"] = "C-V"

    # powertoys用の設定
    keymap_powertoys = keymap.defineWindowKeymap(exe_name="PowerToys.PowerLauncher.exe")

    keymap_powertoys["LC-F"] = "Right"
    keymap_powertoys["LC-B"] = "Left"
    keymap_powertoys["LC-A"] = "Home"
    keymap_powertoys["LC-E"] = "End"
    keymap_powertoys["LC-H"] = "Back"
    keymap_powertoys["LC-D"] = "Delete"
    keymap_powertoys["LC-K"] = "S-End", "Back"
    keymap_powertoys["LC-N"] = "Tab"
    keymap_powertoys["LC-P"] = "S-Tab"

    # エクスプローラで名前変更する時（F2）
    keymap_rename = keymap.defineWindowKeymap(exe_name="explorer.exe", class_name="Edit")

    keymap_rename["LC-F"] = "Right"
    keymap_rename["LC-B"] = "Left"
    keymap_rename["LC-A"] = "Home"
    keymap_rename["LC-E"] = "End"
    keymap_rename["LC-H"] = "Back"
    keymap_rename["LC-D"] = "Delete"
```

LC：左のCtrlキー
S：shiftキー


注意事項：
- keymap.editorにはフルパスを指定する。バックスラッシュは二重で。
- 完全なkill lineができない。末尾の改行コードまで削除してしまう。
- Ctrlキーについて、「C」じゃなくて「LC」のように左（L）なのか右（R）なのかを書かないとうまく動かない。
- 変換キーをMacBookのcommandキーのように使う（モディファイアキーの設定）
- タブ操作やコピペ操作など、既に使いやすいショートカットキーが存在するものについても、個人の好みでMacBook風に変更した。

# Keyhacをスタートアップに設定
keyhac.exeのショートカットをスタートアップフォルダに置く。

スタートアップフォルダは、ファイル名を指定して実行（Windowsキー+r）で「shell:startup」と入力すれば開ける。

（ちなみに、Keyhac関連のファイル一式を含んだフォルダはC:\Users\TarouYamada\AppData\Roamingに置いた）

# config.pyについて詳しく
以下、上記のconfig.pyを書くのに必要な知識について説明する。詳しくは[公式ドキュメント](https://crftwr.github.io/keyhac/doc/ja/index.html)を参照。

## defineWindowKeymapの引数

```python
keymap.defineWindowKeymap(exe_name="notepad.exe", class_name="RichEditD2DPT")
```

`exe_name`に対象アプリのexeファイル名、`class_name`にクラス名を指定する。

exeファイル名、クラス名はどちらも**Keyhacのコンソールウインドウ**に表示される。

### メモ帳の場合の具体例

Keyhacアイコンを右クリックして「内部ログをON」にする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2659122/8be4d6a9-f971-4d40-19ae-08ebcb713630.png)

その後Keyhacのアイコンを普通にクリックしてKeyhacのコンソールウィンドウを表示する。

この状態で、メモ帳上でテキストを入力する。そうすると以下画像のようにexeファイル名とクラス名が表示される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2659122/18ae4794-0cb3-e691-28e8-c7246a714af0.png)

うまく動かない場合は、exeファイル名だけでクラス名を書かないで試してみる。PowerToys Runについては上記の方法で調べたクラス名を指定してもうまく動かず、結局exeファイル名だけを指定してきちんと動いた。

コンソールウィンドウはこのようにキー入力の解析ができる。

他のアプリ、場所におけるショートカットキーを設定したい場合も、内部ログON→コンソールウィンドウ表示の状態でキー入力すればよい。

## モディファイアキー

「Ctrl+c」のように、同時に押すことで機能する特殊なキーのこと。

今回は、「変換キー+c」のようなショートカットキーを作成したい、つまり変換キーをモディファイアキーとして使えるように設定する：

```python
keymap.defineModifier(28, "User0")
```

`defineModifier`メソッドの第一引数に対象のキー、第二引数に`User0`~`User4`を書く（独自のモディファイアキーは4つまでしか設定できない）。

「28」は変換キーのキーコード。キーコードはexeファイル名やクラス名を調べる時と同様に、入力時にコンソールウィンドウに表示される。

## カンマによる連続したキー入力

Ctrl+k（カーソルより右を削除）は、「Shift+下矢印キーを入力してからバックスペース入力」という一連の動作として設定している：

```python
keymap_notepad["LC-K"] = "S-End", "Back"
```

カンマで区切れば一連の連続したキー入力の意味になる。

## 既存のショートカットキーの割り当て

コピーのショートカットキーを「変換キー+c」に設定する。

既存のショートカットキー（この場合はCtrl+c）がある場合は、下のように既存のショートカットキーを指定する：

```python
keymap_chrome["User0-C"] = "C-C"
```

※User-0+cでもCtrl+cでもどちらもでもコピー可能。

# 余談

Macを普段使いしていて上記のショートカットキーに慣れている。それができないWindowsではいちいち矢印キーに小指を伸ばす必要がありとてもイライラする。カーソル移動をフォームポジション崩さずに行うには必須の設定。

kill lineについて、たぶん改行コードを含まずに削除できる。もう少しキー入力を解析して試行錯誤すればできそう。

Keyhacにpythonが内蔵されているのでWindows自体にpythonをインストールする必要はないはず。