英日キーボードが混在する環境で生きる：IME切り替え

# 英日キーボードが混在する環境で生きる
新しいノートPCを買ったらキーボードが英字キーボードでした。

手持ちの外付けキーボードは日本語キーボードばっかり。

英字キーボードは以前使っていたから、日本語キーボードも印字の違いは気にせずに使う。

問題は、IMEの切り替えだ。

文章を入力しているときに、「今IMEがオンかオフか」はあまり気にしたくない。あるキーを押せばオン、別のキーを押せばオフにしてほしい。

英字キーボードの場合は左右のAltだし、日本語キーボードの場合は、無変換と変換を使いたい。

# いくつかの選択肢
[こちら](https://mastdesign.me/20240107-jiskeyboard-uskeyboard/)に、英日混在環境の作り方がきちんと書いてある。

まあ、この通りにすればいいんだけど、Alt+Tabがどうも使えなくなるみたい。僕はかなりAlt+Tabを使うので、ストレスが大きくなってしまった。



というわけで、自分でやることにした。

候補は[Keyhac](https://sites.google.com/site/craftware/keyhac-ja)と[AutoHotKey](https://www.autohotkey.com/)。

ChatGPTに聞きながら、両者を並行してスクリプトを作成してみた。最終的になんとなくうまく行ったのが、Keyhacの方だ。AutoHotKeyの方が、OSのより深いレベルで制御しているそうなので、Keyhacでどうしようもなくなれば、AutoHotKeyに頼るべきなのかも知れない。ただ、KeyhacはスクリプトをPythonで書けるということもあり、先にKeyhac版が完成した。

開発の悩みは、Altを押すとMenuにフォーカスが移ってしまうという挙動の制御。Altをもう一度押すと良いアプリとか、Escを押すべきなアプリ、はたまたフォーカスが移らないアプリなど、いろいろだ。

[上記サイト](https://mastdesign.me/20240107-jiskeyboard-uskeyboard/)で紹介されていた[TAALT](https://www.gigafree.net/utility/keyboard/TAALT.html)というツールを使えば、この問題は一発で解決する。が、別のツールを入れずにKeyhacかAutoHotKey単独で実現するならそれがベストだ。

そのへんの制御を入れつつ、作っていったが、MicrosoftOfficeの挙動がまた特殊で、事前にESCを送信する処理をいれるとうまく行った。が、Officeのばあい、ESCを送出するとダイアログが閉じてしまうので、ファイル名等の入力には使えない。この問題は未だに解決できていない。

# 解答例
この記事はエッセイではないので、僕の現時点での解答を以下に示す。

```python:keyhac.py
import sys
import os
import datetime
import time

import pyauto
from keyhac import *

def configure(keymap):
    # グローバルキー設定
    keymap_global = keymap.defineWindowKeymap()
    # 特定アプリケーションでESCなどを送信する処理
    office_apps = ["winword.exe", "excel.exe", "powerpnt.exe"]
    esc_apps = office_apps + ["chrome.exe", "notepad.exe"]
    # C-h l でキーログを見る

    def set_ime(status):
        if status in [0, 1]:  # 有効な値か確認
            keymap.getWindow().setImeStatus(status)
            #print(f"IME is {'ON' if status == 1 else 'OFF'}")
        else:
            print("Invalid status: Use 0 (OFF) or 1 (ON)")

    def set_ime_on_simple():
        set_ime(1)
    def set_ime_off_simple():
        set_ime(0)

    # IMEをオンにする関数
    def set_ime_on():
        active_window = keymap.getWindow().getProcessName()
        if any(app in active_window.lower() for app in office_apps):
            keymap.InputKeyCommand("Esc")()  # ESCキーを送信
        set_ime(1)
        if any(app in active_window.lower() for app in esc_apps):
            time.sleep(0.5)
            keymap.InputKeyCommand("Esc")()  # ESCキーを送信
        elif "emacs.exe"  in active_window.lower():
            return
        else:
            keymap.InputKeyCommand("Alt")()  # Altキーを再送信してフォーカスをリセット

    # IMEをオフにする関数
    def set_ime_off():
        active_window = keymap.getWindow().getProcessName()
        if any(app in active_window.lower() for app in office_apps):
            time.sleep(0.2)
            keymap.InputKeyCommand("Esc")()  # ESCキーを送信
        set_ime(0)
        if any(app in active_window.lower() for app in esc_apps):
            keymap.InputKeyCommand("Esc")()  # ESCキーを送信
        elif "emacs.exe"  in active_window.lower():
            return
        else:
            keymap.InputKeyCommand("Alt")()  # Altキーを再送信してフォーカスをリセット

    # 右Altキー単押しでIMEをオンに設定
    keymap_global["O-RAlt"] = set_ime_on  # IMEオンの処理を呼び出す
    # 「変換」キーでIMEをオンに設定
    keymap_global["O-(255)"] = set_ime_on_simple

    # 左Altキー単押しでIMEをオフに設定
    keymap_global["O-LAlt"] = set_ime_off
    # 「無変換」キーでIMEをオフに設定
    keymap_global["O-(235)"] = set_ime_off_simple
```