にせねこメモ

はてなダイアリーがUTF-8じゃないので移ってきました。

キーボードの言語によってDvorakJの有効無効を切り替える

随分前に、DvorakJを利用して英字入力はDvorakにし、日本語入力はJapanist 2003を利用して親指シフト(NICOLA)をエミュレーション利用していた。一方で、ロシア語やタイ語等を入力する要求がでてきて、DvorakJはこの様な日本語以外のキーボード言語の入力時に無効にする設定ができず、まともに入力ができないため、Dvorak配列を使用するの自体をやめてしまっていた。

やりたいこと

キーボードの配列を次のようにしたい。

  • 日本語入力時(キーボード言語が日本語かつIMEオン): 親指シフト(NICOLA)
  • 英字入力時(キーボード言語が日本語かつIMEオフ): Dvorak
  • 他言語入力時(キーボード言語が日本語以外): Qwerty相当 (キーボードの入力そのまま)


これは、2回の場合分けによって表現できる。

  1. キーボード言語による場合分け(日本語か?)
    • 日本語でない→キー変換を行わない
    • 日本語である→2へ
  2. IMEのオンオフによる場合分け
    • オン→日本語入力用配列
    • オフ→直接入力用配列


ここで、現行のDvorakJ (2014-06-07版)ではIMEのオンオフの検出で日本語入力時と直接入力時で別々の配列を適用することができる(つまり、2.は実装されている)が、一方で日本語以外の言語に切り替えたときも直接入力用の配列が適用されてしまって、日本語以外の言語を入力することができない。
なので、うまいことキーボードの言語が日本語であるかどうかで場合分けを行い、日本語である場合にだけキーリマップを適用する様にしたい。

方針

DvorakJはAutoHotkey_L用のスクリプトが公開されているので、それを改変し、条件分岐部分を追加すればいいのではないかと思う。

AutoHotkeyで現在のキーボード言語を取得するには次のページのようにするといいらしい。

ここに挙げられたコードを引用する。ただし、このコードはコンソールでは使えないとのことである。

F11::
  SetFormat, Integer, H
  WinGet, WinID,, A
  ThreadID:=DllCall("GetWindowThreadProcessId", "UInt", WinID, "UInt", 0)
  InputLocaleID:=DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
  MsgBox, %InputLocaleID%
Return

要するにInputLocaleIDが現在のキーボードを識別するIDのようである。

WinAPIのGetKeyboardLayoutの項によると、

The return value is the input locale identifier for the thread. The low word contains a Language Identifier for the input language and the high word contains a device handle to the physical layout of the keyboard.

GetKeyboardLayout function (Windows)

だそうで、下位2バイトは入力言語のlanguage code、上位2バイトはキーボードレイアウトへのデバイスハンドルらしい。

最新のWindows 10 (バージョン1709)において、実際に取得したInputLocaleIDは次のようになっている。

言語 キーボード InputLocaleID
日本語 Microsoft IME 0x4110411
タイ語 タイ語 Kedmanee 0x41E041E
ロシア語 ロシア語 0x4190419
ロシア語 ロシア語 - ニーモニック 0xF0330419
ロシア語 ロシア語 (タイプライター) 0xF0080419
モンゴル語 伝統的なモンゴル文字 0xF0B20850

その言語の標準的なキーボード配列においては上4桁(2バイト)は下4桁と同じになっているように見える。
キーボードによって条件を変化させるのであれば全体を比較すればよいし、言語によって変化させるのであれば下2バイトだけ比較すればよいことになる。

Language Codeについては、AutoHotkeyのドキュメントにも次の様な記術があるが、

WindowsのLanguage Codeの一覧は次のページから参照することができる。


DvorakJのソースをみてみたところ、DvorakJ全体を一時停止するキーショートカットが設定できるので、これに使われてるロジックを利用して、キーボードが日本語以外に切り替わった場合に一時停止するようにし、反対に日本語に切り替わった場合には一時停止を解除するようにしてみる。

環境

改変

DvorakJのAutoHotkey_Lスクリプト版の src/init/set_various_timers.ahk を開き、27行目付近の

	;;; IME の状態を定期的に調べる
	SetTimer, IME_GET, %IMEms%

の下に

	SetTimer, Keyboard_GET, %IMEms%

を挿入し、更に末尾に

;; キーボードの言語を一定間隔毎に取得し、日本語以外だったら動作を停止する。
;; 日本語だったら動作を再開する。
Keyboard_GET:
  SetFormat, Integer, H
  WinGet, WinID,, A
  ThreadID:=DllCall("GetWindowThreadProcessId", "UInt", WinID, "UInt", 0)
  InputLocaleID:=DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
  If ( InputLocaleID & 0xFFFF = 0x411 ){ ;Japanese
    If ( A_IsSuspended ){
      toggle_status_of_dvorakj(False)
    }
  } Else { ;otherwise
    If ( !(A_IsSuspended) ){
      toggle_status_of_dvorakj(True)
    }
  }
return

を追加した。

要するに、一定時間ごと(今回はIMEの状態を定期的に調べる間隔(IMEms)を流用した)に現在のキーボードの入力言語を取得し、

  1. 入力言語が日本語であり、かつ、DvorakJが無効である(Suspendされている)場合は、Suspendを解除する
  2. 入力言語が日本語でない、かつ、DvorakJが有効である(Suspendされてない)場合は、Suspendする

という処理をもともとのソースコードに追加している。

感想・課題

しばらく使ってみたが、親指シフト回りがあまり快適でなかったのでやめてしまった。DvorakJ自体を利用するのが久しぶりであるのに加え、親指シフトのエミュレーション用として利用したことがなかったため、問題の切り分けができていない。そのためはっきりしたことが言えないので今後も検証が必要である。

  • 親指シフトのエミュレーションの同時押しの検出がやまぶきRより精度が低い感じがする。そのため、入力ミスが多発した。
    • 急いで入力すると「にゅうりょく」が「にけをりょく」や「にゅうりいる」になったり。やまぶきRでも時々なるけれど…。
    • やまぶきRと同様の設定にしてるつもりであるが、設定によって改善が可能かもしれない。要検証。
  • ウィンドウを切り替えたときや別の入力フィールドに移ったときなどにうまく入力ができなくなる場合があった。
    • 改変部分が原因かもしれないが、素のDvorakJを最近使ってなかったので比較ができない。
    • やまぶきRでもなったことがあるように思うので一概に何ともわからない。別のウィンドウ切り替えた後で戻すと復活したりする。
  • DvorakJに用意されていた一時停止機能を勝手に利用しているため、一時停止してもキーボードが日本語であればすぐ再開されてしまい、一時停止機能が使えない状態になっている。
  • 本当にこんな改変で大丈夫なのか……?


頂いた情報によると、DvorakJとやまぶきRではいろいろと同時押しエミュレーションのロジックが異なるので入力感は同じにならないとのこと。


なかなか難しい。