同時打鍵ステートマシン


同時打鍵ステートマシンの説明

目次に戻る

はじめに

以下は、親指ひゅんQ(4.30)の心臓部ともいえる、同時打鍵ステートマシンを図示したものです。実際には、この図に示されていない2つの状態(シフトキー押下中状態、Ctrl+文字キー押下中状態)がありますが、あまり重要ではないし、ややこしいので省略してあります。
矢印の付け根にある、E11という記述は、状態遷移の原因となるイベントおよび付帯する処理を表しています。
同時打鍵ステートマシン

ステートマシンが用いる変数およびタイマ
名称 説明および用途
moji文字キーを記憶するための変数
repeat_mojiリピートする文字キーを記憶するための変数
oyayubi親指キー記憶するための変数
repeat_oyayubiリピートする親指キー記憶するための変数
time1イベント発生時刻を記憶するための変数
time2イベント発生時刻を記憶するための変数
event_timer時刻の経過に伴ってリニアにデクリメントされるタイマ変数。値が0になった時点で、ステートマシンに対してタイマイベントを発生する(ワンショット)。なお、ステートマシンに対して簡易ロジックを指定している場合、event_timerへの値のセットは行われません。
current_time現在時刻をもつ変数
現在時刻(current_time)は、インスタンス化時(NULL状態から初期状態への遷移=イベントE00)を0とし、10msec程度の分解能をもつ時計によってリニアにインクリメントされる。タイマ変数event_timerは、0より大きな値をもつ場合、現在時刻のインクリメントに伴って、リニアにデクリメントされる。

ステートマシンの制御用に外部から与える定数
名称 説明および用途
e_charTime文字->親指同時打鍵検出許容期間(簡易ロジックでは無効)
e_oyaTime親指->文字同時打鍵検出許容期間(簡易ロジックでは無効)
e_nicolaTime親指単独打鍵みなし期間

簡易ロジックとは、ステートマシンを駆動するためのイベントタイマを使わない動作モードです。イベントタイマを使わないことで、タイマ資源が不安定な環境での実装を容易にします。イベントタイマを用いる代わりに、キーボードハードウェアがもつタイパマチック遅延時間によって、同時打鍵を許容する期間が決まります。PC互換機やHHPCの場合は最小値が約250msecであり、キーボード入力時にほとんど違和感を感じることはありません。

初期状態
イベントコード イベントの説明 処理
E11 文字キーの押下を検出。 検出した文字キーをmojiにセット。time1に時刻を記憶。event_timere_charTimeをセット。
E12 親指キーの押下を検出。 検出した親指キーをoyayubiにセット。time2に時刻を記憶。event_timere_oyaTimeをセット。
初期状態に遷移した時点で、current_timeを除くすべての変数はリセットされる。
文字キー押下中状態
イベントコード イベントの説明 処理
E21 文字キーのリリースを検出。または、対象外のキー入力を検出 mojiを出力。初期状態に遷移
E22 キーボードリピートを検出 mojiを出力。mojirepeat_mojiにセット。リピート状態に遷移
E23 タイマイベントを検出 mojiを出力。mojirepeatmojiにセット。リピート状態に遷移。
簡易ロジックでは、このイベントは発生しません。
E24 親指キーの押下を検出 検出した親指キーをoyayubiにセット。time2に時刻を記憶。event_timere-oyaTimeをセット。文字キー親指キー押下中状態に遷移
E25 文字キーの押下を検出 mojiを出力。検出した文字キーをmojiにセット。time1に時刻を記憶。event_timere-charTimeをセット。文字キー押下中状態に遷移
親指キー押下中状態
イベントコード イベントの説明 処理
E31 親指キーのリピートを検出。 oyayubirepeat_oyayubiにセット。リピート状態に遷移
E32 タイマイベントを検出 oyayubiを出力。oyayubirepeat_oyayubiにセット。リピート状態に遷移。
簡易ロジックでは、このイベントは発生しません。
E33 文字キーの押下を検出 検出した文字キーをmojiにセット。moji+oyayubiを出力。oyayubirepeat_oyayubiにセット。mojirepeat_mojiにセット。リピート状態に遷移
E34 親指キーの押下を検出 oyayubiを出力。検出した親指キーをoyayubiにセット。time2に時刻を記憶。event_timere-oyaTimeをセット。文字キー親指キー押下中状態に遷移
E35 親指キーのリリースを検出。または、対象外のキーの押下を検出 oyayubiを出力。初期状態に遷移
文字キー親指キー押下中状態
イベントコード イベントの説明 処理
E41 キーのリピートを検出(親指キーのみしかあり得ない) moji+oyayubiを出力。mojirepeat_mojiにセット。oyayubirepeat_oyayubiにセット。リピート状態に遷移
E42 タイマイベントを検出 moji+oyayubiを出力。oyayubirepeat_oyayubiにセット。mojirepeat_mojiにセット。リピート状態に遷移。
簡易ロジックでは、このイベントは発生しません。
E43 文字キーの押下を検出し、current_time - time2 < time2 - time1を満たす場合。 mojiを出力。oyayubi+検出した文字を出力。oyayubirepeat_oyayubiにセット。検出した文字キーをrepeat_mojiにセット。リピート状態に遷移
E44 親指キーの押下を検出 moji+oyayubiを出力。検出した親指キーをoyayubiにセット。time2に時刻を記憶。event_timere-oyaTimeをセット。親指キー押下中状態に遷移
E45 親指キーのリリースを検出。または、対象外のキーの押下を検出 moji+oyayubiを出力。初期状態に遷移
E46 文字キーのリリースを検出し、(current_time - time2 <= e_nicolaTime) && (time2 - time1 > current_time - time2 ) を満たす場合。 mojiを出力。親指キー押下中状態に遷移。oyayubiが維持されていることに注意
E47 文字キーのリリースを検出。E46の条件を満たさない場合 moji+oyayubiを出力。初期状態に遷移
E48 文字キーの押下を検出し、current_time - time2 >= time2 - time1を満たす場合。 moji+oyayubiを出力。検出した文字キーをmojiにセット。event_timere-charTimeをセット。文字キー押下中状態に遷移
リピート中状態
イベントコード イベントの説明 処理
E51 リピートを検出 repeat_oyayubi+repeat_mojiを出力。リピート中状態に遷移。repeat_oyayubiまたはrepeat_mojiのいずれかがセットされていない場合は、セットされている側の単独出力となる
E52 親指キーのリリースを検出。または対象外のキー入力を検出 初期状態に遷移
E53 文字キーのリリースを検出 初期状態に遷移
E54 親指キーの押下を検出(リピート中以外のキーしか発生しない) 検出した親指キーをoyayubiにセット。time2に時刻を記憶。event_timere-oyaTimeをセット。親指キー押下中状態に遷移
E55 文字キーの押下を検出(リピート中以外のキーしか発生しない) 検出した文字キーをmojiにセット。time1に時刻を記憶。event_timere-charTimeをセット。文字キー押下中状態に遷移

文字キー親指キー押下中状態の付加説明

文字キー押下中親指キー押下状態ではイベントに対するアクションを決定する際に、いくつかのタイミング要素が加味されています。
E43およびE48
これらは、文字キーを押して親指キーを押した後、いずれのキーも離す前に別の文字キーを押下した、という事象を表しています。各キーの押下をタイミングチャートで示すと、以下のようになります。

文字キー親指キー押下中文字キー押下検出

このように3つのキーがいわば同時に押された場合、最初に押された文字1を親指キーとの同時打鍵として打ち込みたいのか、あるいは、2番目に押された文字キーを同時打鍵として打ち込みたいのか、操作者の意図を判定する必要があります。ステートマシンは、3つのキーの打鍵時刻の差(図のd1およびd2)を比較することで判断します。
E43は、d2 < d1 のとき、つまり親指キーを押してすぐに文字2を押した場合なので、文字1は単独打鍵として処理し、文字2を親指同時打鍵として処理して出力します。E48はその逆で、文字1を押してすぐに親指キーを押したので文字1+親指を出力し、文字2は文字キー押下中状態内の次のイベントによって処理されることになります。
E46およびE47
これらは、文字キーを押して親指キーを押した後、親指キーを離す前に文字キーを離した、という事象を表しています。タイミングチャートで示すと以下のようになります。

文字キー親指キー押下中文字キーリリース検出

もともとE46というイベント検出は過去の親指シフト同時打鍵処理にはありません。常にE47の処理、つまり文字+親指の同時打鍵として処理しておしまい、ということになっていました。
E46が必要になったのは、親指キーと変換/無変換/空白を共用するようになったとき、親指キー単独打鍵のつもりが同時打鍵になりやすい、という現象がおきました。これを解決し、文字や親指を単独打鍵として処理するためでした。E46のイベント検出条件を書き換えると、以下のようになります。
(d2 < e_nicolaTime) && (d1 > d2)

この式の気持ちは、文字キーが単独で押されている時間が長く、かつ、文字キーを離そうとしてるときに親指キーを押したんだから、文字も親指も単独キーとみなしてちょ、というものです。
e_nicolaTimeの値は、親指ひゅんQ4.30では、プロパティ/タイミングタブ/文字-親指-文字オフみなし親指単独打鍵時間 という項目で設定できます。

改訂記録