第1章:なぜ親指シフトを自分で作るのか

親指シフトでは、二つのキーを、ピアノの和音のように、同時に合わせて押します。一方の手で文字キーを押しながら、もう一方の手で親指キーをそっと添えると、単独で押したときとは違う文字が生まれます。キーの位置ではなく、二つのキーが「間に合っているかどうか」が、出てくる文字を決めます。ここに、普通のキーボード配列とは違う難しさがあります。

二つのキーが「間に合う」かどうかで、文字が変わる

一般的なキーマッピングでは、キーと文字は一対一で対応します。Aキーを押せばAが出る、という単純な表があれば十分です。ShiftキーやCtrlキーとの組み合わせも、押している間だけ状態が変わるという意味では、やはり静的な表の延長にあります。

しかしNICOLA方式の親指シフトでは、文字キーと親指キーのあいだの時間差そのものが、変換結果を左右します。近いタイミングで押せば同時打鍵として扱われ、間隔が開けば単独打鍵として扱われます。押しているかどうかという二値の状態だけでは、この判定を表現できません。

つまり実装すべきものは、キーから文字への表ではなく、時間差を読み取る判定です。ここでいうキーボードフックとは、OSが発生させるキー入力を、本来の送り先に届く前に横取りし、自分のプログラムで内容を確認・加工できる仕組みを指します。キーボードフックが受け取れるのは、個々のキーの押下・離上イベントと、そのタイムスタンプだけです。そこから「二つのイベントは同時打鍵と呼べる関係にあるか」を組み立てる必要があります。この判定を具体的にどう組んだかは、後の章で扱います。ここではまず、親指シフトが「配列表」の話ではなく「タイミング」の話だという点だけを押さえておきます。

単純なキーリマッパーの多くは、キーを別のキーへ静的に置き換えることを前提に作られています。その前提に、同時打鍵の判定という要件を後から足すのは、機能追加というより設計の作り直しに近い作業になります。ゼロから実装を選んだ理由をこの一点だけに帰する記録は残っていませんが、現在振り返ると、この構造的な違いは無視できない理由の一つだったと思います。

既存のエミュレータが記録していた「困りごと」

Windows向けの親指シフトエミュレータは、すでにいくつも存在していました。設計文書の冒頭には、次のような目的が書かれています。

Windows 上で動作するキーボード配列エミュレータを Rust で開発する。既存の「やまぶき」「DvorakJ」と同等の機能を持ち、NICOLA(親指シフト)を含む任意のキー配列をエミュレートできる常駐型ツールを目指す。

既存のツールと同じ機能を持つことが、最初の目標として明記されています。参考にしたプロジェクトも同じ文書に列挙されています。kanataからはLLHOOKバックエンドの実装パターン、スキャンコードベースの入出力、チャネルによる入力受付と処理ループの分離、シミュレーションテスト基盤を参考にしたと記録されています。特に、kanataがplatform-known-issues.adocという文書にまとめていたWindowsのLLHOOK固有の既知問題は、後述するガード設計に直接反映されたとあります。やまぶきからはNICOLA配列の挙動仕様と設定体系を、DvorakJからは機能範囲を、それぞれ参考にしたとされています。

自作を選んだ理由は、記録より先に、私自身の使用経験にありました。もともと使っていたのはやまぶきRでした。ところが定期的にプロセスが止まり、そのたびに手動で再起動する必要がありました。Zoomなど一部のアプリとは相性が悪く、うまく動かない場面もありました。そしてもう一つ、Windows向けの親指シフトキーボードソフトで、開発が継続されているものがそもそも見当たらないという事情もありました。使い続けるための選択肢が、実質的に自分で直す以外になかったということです。

なぜ止まりやすかったのか、当時は理由が分かっていませんでした。やまぶきはソースコードが公開されていないクローズドソースのソフトウェアです。本書の執筆にあたり、配布されている実行ファイルの中身(呼び出している外部関数の一覧であるインポートテーブルと、内部に埋め込まれた文字列)を解析にかけたところ、理由の一端が見えてきました。旧版のやまぶき4は、キーボードフックのコールバックの中でIME操作のAPI(ImmSetConversionStatus)を直接呼び出し、同時打鍵かどうかの判定にはSleepを使って、フックのスレッドそのものを一時停止させていました。やまぶきRでは、IME状態を直接書き換える呼び出しは姿を消していましたが、タイミング判定のためのSleepだけは、フックの中に残ったままでした。フックコールバックは、Windowsの仕様上、短時間で処理を返さなければ強制的に解除されます。たとえるなら、フックは玄関先で来客の用件を一瞬だけ確認する係のようなもので、そこで来客を長く引き止めれば、OSはその係ごと持ち場から外してしまいます。Sleepでスレッドを止める処理は、まさにこの「短時間で返す」という前提に反していました。問題の一部にだけ手を入れ、フックの中で時間のかかる処理をするという前提そのものには触れなかった、ということです。

この問題は、awase固有のものでも、Windowsに固有のものでもありません。重い処理をしているタブを開いたまま他の操作をすると、ブラウザ全体が「応答なし」になって固まることがあります。多くのブラウザは、画面の描画やクリックへの反応を、たった一つのスレッド(単一のイベントループ)で順番にさばいており、どこか一箇所が時間をかけすぎると、他のすべての反応が止まってしまうのです。スマートフォンのアプリでも同じ理由で「アプリが応答していません」という警告が出ることがあり、Androidにはこれを検知して必要なら強制終了する仕組み(ANR、Application Not Responding)が組み込まれています。音楽制作用のオーディオインターフェースが一定間隔で必ず呼び出すコールバックも同様で、この中で時間のかかる処理をすると、音がプチプチと途切れる原因になります。呼び出される場所の種類は違っても、「決まった時間内に必ず制御を返す場所」に重い処理を持ち込んではいけないという制約そのものは、GUIのイベントループにも、スマートフォンのOSにも、音声処理にも共通しています。やまぶき4・やまぶきRが同時打鍵の判定という重い処理をフックの中に置き続けていたのは、この共通の制約を破っていたということです。

この個人的な経験は、後日のADR(Architecture Decision Record)にも、より広い形で裏付けられています。2026年3月30日夜のコミット2835bf6「Address all known issues from other thumb shift emulators」には、他の親指シフトエミュレータで報告されていた問題が整理されています。

問題他ソフトの状況awaseの対応
フック消失紅皿でPC高負荷時にキーボードがフリーズすると報告されていたハートビート監視を置き、10秒無応答で警告を出す
管理者権限プロセス紅皿v0.1.3で管理者昇格オプションが追加されていたトレイメニューに「管理者として再起動」を用意する
キーボードレイアウト変更2025年4月のWindows Updateで106→101配列に変わる事例が多数報告されていた起動時にレイアウトを確認し、変更イベントも監視する
画面ロック/セッション切替やまぶきRでスリープ復帰後に動作しなくなる報告があった電源・セッション切替イベントで状態を丸ごと引き直す
修飾キーのスタック残り調査した全エミュレータに共通する問題として記録されていたフォーカス変更時に、キーの押下状態を実際の状態へ同期し直す
カタカナ/ひらがなキーロック型のキーでキーアップが来ず、親指キーには使えないと記録されていた設定を検証し、IMEガードとして扱えるキー数の上限を設ける

これらは、他ソフトの不具合を非難するための一覧ではありません。「常駐して全キー入力を横取りするツールが、どこで壊れやすいか」を先に洗い出した、いわば失敗のカタログです。プロジェクト開始から2日後というタイミングでこの棚卸しが行われている点も見落とせません。同時打鍵の判定そのものより先に、壊れ方の一覧を作ることが優先されていました。

当時のADRは、これらを「他ソフトが抱えていた不具合」として淡々と整理しているだけで、私がどの瞬間にどう感じたかまでは書き残していません。ただ、やまぶきRで実際に手を止められた経験があったからこそ、この一覧を作る優先順位は自然に決まっていました。欲しかったのは変換の巧妙さそのものより、日常的に使うツールが些細な不具合で入力不能に陥らないという安心感でした。

「awase」という名前と、最初に作ろうとした範囲

このツールには「awase」という名前が付けられました。最初のREADMEには、名前の由来が次のように書かれています。

awase (合わせ) remaps physical keys based on simultaneous keystroke detection — pressing a character key and a thumb key at the same time produces a different character, like playing a chord on a piano.

「合わせ」は、複数のキーを合わせて押すという動作そのものを指す名前です。ピアノの和音を弾く感覚にたとえる表現は、このREADME初版から一貫して使われています。冒頭で述べた「二つのキーを合わせて押す」という説明は、この由来をなぞったものです。技術的な仕組みの名前ではなく、指の動きの名前が選ばれている点は、このツールが最初から使う側の体感を軸に語られていたことを示しています。

この名前には、後からもう一つの理由が加わっています。2026年6月15日、プロジェクトの公式サイト(awase.cc)がリニューアルされ、「名前の由来」という説明が追加されました。「awase」をQWERTYキーボードで打つと、awaseの五文字は、左手の小指→薬指→小指→薬指→中指という順に、外側から内側へ流れるように打鍵できます。同時打鍵を「合わせる」という意味に加えて、キーボード上で指が美しく動く並びでもあった、という遊び心が、README初版から3か月近くたってから明かされたことになります。

最初に作ろうとした範囲は、NICOLA専用のツールではありませんでした。設計文書が掲げていたのは、やまぶきやDvorakJと同等の機能を持ち、NICOLAを含む任意のキー配列をエミュレートできる常駐型ツールです。つまり、親指シフトはこのツールが対応する配列の一つという位置づけで、最初から一般的なキー配列変換の器として構想されていました。同時打鍵の判定はその器の中でも特に難しい一機能という扱いであり、それ自体が目的として単独で切り出されていたわけではありません。

この器の中身をどこまで固く作るかは、まだ何も決まっていませんでした。同時打鍵の判定をどう表現するか、壊れたときに何を優先するか、といった具体的な設計は、これから手を動かしながら決めていくことになります。実際に手を動かし始めてから、最初の壁にぶつかるまでにかかった時間は、ごくわずかでした。


設計原則:正しく変換するより、入力を失わないことを優先する

常駐型の入力ツールにとって、「正しく変換する」ことより「入力を失わない」ことを優先する。

この原則が働くのは、すべてのキー入力を横取りしてから加工して出す、常駐型のツール全般です。加工の巧拙よりも先に、加工そのものが止まったときに何が起こるかを考える必要がある場面に適用されます。

実装の形は三段階です。正常時は配列変換を適用します。異常を検知した時点では、変換を諦めて元の入力をそのまま通します(PassThroughフォールバック)。重大な異常が続く場合は、変換そのものを自動停止し、トレイ通知でユーザーに知らせます。どの段階でも、キーボードから入力そのものが失われることはなく、再開はユーザーの明示的な操作を待ちます。

この原則が保証するのは、不具合時にキーボードが使い物にならなくなる事態を避けることだけです。変換そのものがいつ正しくなるか、同時打鍵の判定がどれだけ的確かという問題には、この原則は答えません。判定を間違えたまま元の入力をそのまま通しても、利用者は文字が化けたことに気づけます。判定を間違えたままキーボードごと沈黙されるより、それは扱いやすい失敗です。この原則が守っているのは正しさではなく、失敗したときに気づける余地であり、正しい判定そのものは、また別の問題として残ります。