QMK Firmwareで擬似的なUS配列を実現する

こんにちは。
DXソリューション開発部の池田です。

最近、開発時のプチストレスがいい感じに改善できたので、その方法を紹介したいと思います。
いわゆる自作キーボード派にしか刺さらない内容ですが、どなたかの参考になれば。

自作キーボードって?

好きな方ならご存知かと思いますが、2018年頃から(一部で)大変流行している自分でパーツを選んで組み立てるタイプのキーボードのことです。物理的な形・キーマップ・キースイッチ・キーキャップなどを自由に選べるので人によっては理想の一品に出会えるかもしれません。

かくいう私も当時のブームにまんまと乗っかり、Corne Cherryという左右分離型のものを愛用しています。

きっかけ

もともとUS配列派なのでこのCorneも当初はUS配列前提でキーマップ設定していました。前職は長らくデスクトップマシンで移動することもほぼなく、OSの設定でキーボードレイアウトを変更するだけで良かったのです。

が、エコモットで働くようになって状況が変わりました。開発マシンが日本語配列のノートPCとなり、結構頻繁に移動して作業するようになりました。(普通の環境になったとも言います)

この場合OSでレイアウトを変更してしまうと、外付けしたCorneで作業している分には良いのですが、ノートPC単体で使う場合に印字と一部キーの出力が合わなくなってしまいます。これは紛らわしく、クリティカルな作業でのミスにも繋がるのでさしあたり日本語配列前提でCorneのキーマップを作り直して乗り切りました。

基本的にはこれで解決です。

改善したかったこと

ただUS配列派の方なら分かっていただけると思うのですが、長年染み込んだ癖のせいで一部の記号入力が地味に辛い。具体的な例でいうと「;」などはUS配列だとシフト押下で「:」となりますが日本語配列だとシフト押下で「+」になります。

キーマップ自体は自由でも、この辺りの動きは先述のレイアウト設定に応じたPC側の解釈によるものなので単純なキーの入れ替えだけでは実現できません。

  • レイヤー機能というものを使ってこれらの記号を切り出し、そちらで入力するようにする → これはこれで慣れが必要
  • MacでいうKarabinerのようなキーカスタマイズ系ソフトで何とかする → PC側にインストールが必要。また管理者モードで起動したアプリやリモデス環境などで上手く動かなかった経験あり

などと色々試行錯誤してはみたのですが、いまいちしっくり来ません。もう諦めて日本語配列に慣れるのが早いか…とは思いつつ、何か上手い方法はないものか考えていました。

諸々まとめると、やりたいことは以下のようになります。

  1. OSのキーボードレイアウトは日本語配列から変えたくない
  2. シフト押下時も含め、US配列における「;」のような挙動をするキーを再現したい(他にもいくつかあり)
  3. できればPC側にキーカスタマイズ系ソフトを入れたくない

QMK Firmwareでいけそう

話が前後しますが、自作キーボードは一般的にQMK Firmwareというものをビルドしてファームウェアを作成し、書き込むことで様々な設定を行います。

単純なキーマップ変更ぐらいしかしていませんでしたが、そういえば他にも色々書いてあった記憶があるので、この辺りなんとかなるのでは?と思い至りました。これで何とかなるならキーボード単体で完結することになるので、上記やりたいことの3番も達成できて完璧です。

調査したところドキュメント内に早速それっぽいものを発見しました。
任意のキーコードの挙動のプログラミング
マクロ

リンク先の説明を読む限り、キーが押されるか放されるたびにkeymap.cの中にあるprocess_record_user関数が呼ばれるようなので、この関数内でシフトキーの押下状態を見て、送信するキーコードを差し替えてあげれば実現できそうです。

なおQMK Firmwareは普通github上でフォークしてから弄るので、site:github.comを指定した上で各種キーワードでググると色んな方の設定がヒットします。似たようなことをやりたい人は結構居そうだったので、参考になりそうなものはないかとprocess_record_userで絞ると、ありがたいことにいくつか見つかりました。

諸々真似させてもらいつつ、最終的に私のkeymap.cは以下のような内容になりました。

簡単な説明

長いのでところどころ省略していますが、まずcustom_keycodesに任意の名前で新しいキーコードを定義します。この例では私の中でUS配列時っぽく動いて欲しかった3つのカスタムキーを追加しています。

追加したキーコードはkeymaps定義内で使えるので、デフォルトのキーコードと同様に配置します。この辺りはもちろん好みです。

最後に件のprocess_record_user関数の中に、追加したキーコードに関する分岐を追加します。その時点で左右シフトキーが押されているかどうかを取得することができるので、その状態を見ながら入力したいキーダウン/アップイベントを送信します。イベント送信についてはマクロ関数が既に用意されているので、キーコードを指定して呼ぶだけでした。

少しややこしいのが、実現したいことの性質上、場合によってはシフト押下状態を一時的に無視する必要があるところです。

上記SCLN_COLNのケースで言うとシフト押下時にこのキーが押された場合に出力したいのは「:」なのですが単純にJP_COLNを送信しただけだとシフト押下もそのままOSに伝わっているので、日本語配列におけるシフト+「:」、つまり「*」が出力されることになります。なのでこういう場合は直前にシフトのキーアップイベントを送信して押下状態を解除、目的のキーイベントを送信してから、後続の入力に影響がないように復元する、という流れになっています。

最初に見たときはタイミング的に大丈夫か?とも思いましたが、ここ何ヶ月か試した感じだと特に問題となるような動きもなく、快適そのものです。何よりPC側に何も設定しなくても接続すればそのままUS配列ライクに動くというのは想像以上にストレスフリーでした。

先人の知恵に感謝ですね!

改善点

ただ一点、このやり方だと定義したキーのキーリピートが働かないことに気づきました。幸い押しっぱなしで入力したくなるようなものはなかったので実用上問題は感じていないのですが、ここまで来たらせっかくなので何とかしたくなります。

少し調べてみた感じ、キーの押下状態をスキャンするために定期的に呼ばれるmatrix_scan_user関数があるので、process_record_userのタイミングでフラグを上げ下げし、押しっぱなしの間はこの関数内でイベントを送信し続けるという方法で実現されている方がいました。

パフォーマンスに影響がありそうなのと、それなりにウェイト調整などが必要そうでまだ試せていませんが、次の課題にしたいですね。

最後に

キーキャップなど見た目のカスタマイズが楽しい自作キーボードですが、やろうと思えばこういった細かい挙動に手を入れられるのも大きな魅力だと感じました。

この記事を読んで、ちょっと触ってみようかという人が増えてくれると嬉しいです。